【算法系列 一】 Linked List

1. 給定兩個鏈表,分別表示兩個非負整數。它們的數字逆序存儲在鏈表中,且每一個結點只存儲一個數字,計算兩個數的和,而且返回該鏈表(Leetcode 2)。

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8算法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int val = (l1.val + l2.val) % 10;
		int flag = (l1.val + l2.val) / 10;
		ListNode l3 = new ListNode(val);
		ListNode res = l3;
		while (l1.next != null && l2.next != null)
		{
			l1 = l1.next;
			l2 = l2.next;
			val = (l1.val + l2.val + flag) % 10;
			flag = (l1.val + l2.val + flag) / 10;
			ListNode l4 = new ListNode(val);
			res.next = l4;
			res = l4;
		}
		while (l1.next != null)
		{
			l1 = l1.next;
			val = (l1.val + flag) % 10;
			flag = (l1.val + flag) / 10;
			ListNode l4 = new ListNode(val);
			res.next = l4;
			res = l4;
		}
		while (l2.next != null)
		{
			l2 = l2.next;
			val = (l2.val + flag) % 10;
			flag = (l2.val + flag) / 10;
			ListNode l4 = new ListNode(val);
			res.next = l4;
			res = l4;
		}
		if (flag > 0)
		{
			ListNode l4 = new ListNode(flag);
			res.next = l4;
		}
		return l3;
    }
}

這個題目很簡單,就此很少作討論,注意一點就是最後兩個數加完,若是有進位,還須要new個結點出來。數組

2. 給定一個單鏈表,翻轉該鏈表(Leetcode 206)

因爲單鏈表翻轉時會丟失節點,因此須要把節點保存起來。spa

翻轉單鏈表須要存儲3個節點,分別是當前節點,當前節點的前面節點,當前節點的後繼節點。.net

注意null時的判斷,最後返還時須要把原始節點的尾節點返回。指針

public class Solution
{
	public ListNode reverseList(ListNode head)
	{
		ListNode reversedHead = null;

		ListNode currentNode = head;
		ListNode prevNode = null;
		ListNode nextNode = null;
		while (currentNode != null)
		{
			nextNode = currentNode.next;
			if (nextNode == null)
			{
				reversedHead = currentNode;
			}
			currentNode.next = prevNode;
			prevNode = currentNode;
			currentNode = nextNode;
		}
		return reversedHead;
	}
}

class ListNode
{
	int val;
	ListNode next;

	ListNode(int x)
	{
		val = x;
	}
}

2.1 給定一個鏈表,翻轉該鏈表從m到n的位置,要求直接翻轉而非申請新空間,只能遍歷一遍。(Leetcode 92)

For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,code

return 1->4->3->2->5->NULL.blog

與翻轉單鏈表不一樣的是,將1->2->3->4->5->NULL轉成1->2<-3<-4<-5->NULL是沒法從尾節點遍歷全部節點的。須要使用另一種思想。排序

總體思想就是頭插法,好比1->2->3->4把2到4翻轉,將結點3插到1和2之間,而後把4再插到1後面。實現翻轉。遞歸

思想很簡單,主要是中間結點不斷再變化位置,因此注意保留不少個位置。leetcode

須要4個變量:待翻轉節點的前一個節點(相似頭節點),待插入節點,待插入節點的下一個節點,插完後的鏈表的最後一個節點。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode res = new ListNode(0);//必需要有空的頭結點,由於若是相似1-2,要變成2-1,沒辦法把2插到1的前面去
		res.next = head;
		ListNode p = res;//待翻轉的結點的前一個結點,每次都是插在p的後面
		for (int i = 0; i < m - 1; i++)
		{
			p = p.next;
		}
		ListNode l = p.next.next;//待插入結點
		ListNode q = null;//下一個待插結點
		ListNode d = p.next;//插完後的鏈表的最後一個結點
		for (int i = 0; i < n - m; i++)
		{
			q = l.next;
			d.next = q;
			l.next = p.next;
			p.next = l;
			l = q;
		}
		return res.next;
    }
}

3. 給定一個鏈表和一個值x,將鏈表劃分紅兩部分,使得劃分後小於x的結點在前,大於等於x的結點在後,在這兩部分中要保持原鏈表中的出現順序(Leetcode 86)。

For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.

因爲上一題的影響,我第一反應就是頭插法,好比例子中的1->4->3->2,把2插到1的後面變成1->2->4->3,每遇到小的一個就頭插一次。

代碼以下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        if (head == null)
		{
			return head;
		}
		ListNode res = new ListNode(0);
		res.next = head;
		ListNode p = res;
		while (p != null)
		{
			if (p.next == null || p.next.val >= x)
			{
				break;
			}
			p = p.next;
		}
		ListNode d = p.next;
		ListNode l = d;
		ListNode b = d;
		while (l != null)
		{
			l = l.next;
			if (l == null || l.val < x)
			{
				break;
			}else {
				b = b.next;
			}
		}
		ListNode q = null;
		if (l != null)
		{
			q = l.next;
		}
		while (l != null)
		{
			if(l.val >= x)
			{
				b = l;
				l = q;
				if(l != null)
				{
					q = l.next;
				}else {
					break;
				}
				continue;
			}
			p.next = l;
			l.next = d;
			b.next = q;
			l = q;
			if(l != null)
			{
				q = l.next;
			}else {
				break;
			}
			p = p.next;
		}
		return res.next;
    }
}

其中如上題設了好多指針,在此解釋一下

p:頭插中的頭

d:大於等於x的第一個結點

b:大於等於x的最後一個結點

l:待插入的結點

q:下一個待判斷的結點

而後對這個複雜的關係畫了半天的圖,終於AC掉了這個問題。

而後一想,分明還有更好的辦法啊。定義兩個指針p1,p2,遍歷一遍鏈表,將小於x的接到p1後面,大於等於x的接到p2後面,最後將p2接到p1後面就行了啊。

代碼以下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode p1 = new ListNode(0);
		ListNode p2 = new ListNode(0);
		ListNode p1tail = p1;
		ListNode p2tail = p2;
		while (head != null)
		{
			if (head.val < x)
			{
				p1tail.next = head;
				p1tail = p1tail.next;
			}
			else
			{
				p2tail.next = head;
				p2tail = p2tail.next;
			}
			head = head.next;
		}
		p1tail.next = p2.next;
		p2tail.next = null;
		return p1.next;
    }
}

這樣代碼簡單多了,思路也更清晰了。

在上面的思路下,這道題目是否是有點像快速排序呢?有關快排或者排序的知識請參kao這裏。選出一個pivot,而後將序列分紅兩邊,一邊比pivot小,一邊比pivot大,而後不斷遞歸。這正是快速排序的思想啊。

該問題其實說明:快速排序對於單鏈表存儲結構仍然使用。

注意:不是全部排序都方便使用鏈表存儲。如堆排序,將不斷的查找數組的n/2和n的位置(父結點與子結點),用鏈表作就不太方便了。

4. 給定排序的鏈表,刪除重複元素,只保留重複元素第一次出現的結點(Leetcode 83)。

For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.

思想很簡單,因爲是排好序的,碰到一個不一樣的值就保存起來,而後比較下一個結點的值是否是和它相同,若是相同則刪掉這個結點,若是不一樣則改變保存起來的值,繼續比較。

代碼以下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null)
		{
			return head;
		}
		ListNode p = head;
		ListNode p1 = head;
		int val = head.val;
		head = head.next;
		while (head != null)
		{
			if(head.val == val)
			{
				p1.next = head.next;
				head = head.next;
			}else {
				val = head.val;
				p1 = head;
			}
		}
		return p;
    }
}

5. 給定排序的鏈表,若發現重複元素,則重複元素所有刪除。(Leetcode 82

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.

思想很簡單,碰到相同的就跳過便可。

代碼以下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null)
		{
			return head;
		}
		ListNode res = new ListNode(0);
		res.next = head;
		ListNode p = res;//最後一個不重複的結點
		while (head != null)
		{
			if (head.next == null || head.val != head.next.val)
			{
				p = head;
				head = head.next;
			}
			else
			{
				while (head.next != null && head.val == head.next.val)
				{
					head = head.next;
				}
				head = head.next;
				p.next = head;
			}
			
		}
		return res.next;
    }
}

系列:

【算法系列 一】 Linked List

【算法系列 二】 Stack

【算法系列 三】 Quene

【算法系列 四】 String

相關文章
相關標籤/搜索