鏈表題目重要順序分類:
2?兩數(shù)相加(ok)
21.?合并兩個(gè)有序鏈表(ok)? ??鏈表? 【類似于題2】
24 : 鏈表交換
92:? 反轉(zhuǎn)鏈表 II (特別重要,比較難)? ? ? ? ??
劍指offer:
24:反轉(zhuǎn)鏈表
25:合并鏈表
52:2個(gè)鏈表第一個(gè)公共節(jié)點(diǎn)
第2等級(jí):
23:? ?合并K個(gè)升序鏈表
141:??環(huán)形鏈表? ? ? ? ? ? ? ? ? ? ? ? ?
142:? 環(huán)形鏈表2? ? ? ? ? ? ? ? ? ? ??
148:? 排序鏈表? ? ? ? ? ? ? ? ? ? ? ? ?
160: 相交鏈表? ? ? ? ? ? ? ? ? ? ? ? ??
143:? 重排鏈表? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
19:? 刪除鏈表的倒數(shù)第 N 個(gè)結(jié)點(diǎn)? ? ??
25:? ?K 個(gè)一組翻轉(zhuǎn)鏈表? ? ? ? ? ?
總結(jié):遞歸,快慢指針比較
鏈表的6大題型:刪除,合并,反轉(zhuǎn),環(huán)形,排序, 翻轉(zhuǎn), 想加
鏈表題目分類:第一類: 刪除類-----------快慢指針
19:? 刪除鏈表的倒數(shù)第 N 個(gè)結(jié)點(diǎn)? ? ???鏈表
public ListNoderemoveNthFromEnd(ListNode head, int n) {
// n并不知道,只有遍歷完成才能得到
? ? ListNode dummy =new ListNode(0, head); // 生成一個(gè)0開始的dummy節(jié)點(diǎn)
? ? ListNode first = head;
? ? ListNode second = dummy;
? ? for (int i =0; i < n; ++i) {// 先把快指針移動(dòng)n。移動(dòng)到了3的位置
? ? ? ? first = first.next;
? ? }
while (first !=null) {// 一直遍歷到快指針結(jié)束
? ? ? ? first = first.next; // 為什么要移動(dòng)這個(gè)????因?yàn)檫@個(gè)才會(huì)移動(dòng)鏈表,或者是遍歷鏈表
? ? ? ? second = second.next;
? ? }
second.next = second.next.next;// 下一個(gè)節(jié)點(diǎn)直接指向下下個(gè)
? ? ListNode ans = dummy.next;// 因?yàn)橐祷仡^節(jié)點(diǎn)
? ? return ans;
}
刪除有序/無序鏈表中重復(fù)的元素(并不重要)
leetcode 83?
第二類: 合并鏈表(21,23)------遞歸
例題:21 Merge Two Sorted Lists 【easy】
題意:將兩個(gè)排序好的鏈表合并成新的有序鏈表
public ListNodemergeTwoListsDigui(ListNode list1, ListNode list2) {
if (list1 ==null) {
return list2;
? ? }
if (list2 ==null)return list1;
? ? if (list1.val > list2.val) {// l2小
? ? ? ? list2.next = mergeTwoListsDigui(list2.next, list1); // 下一個(gè)元素是:自己的下一個(gè)和大的元素比較
? ? ? ? return list2;
? ? }else {// l1 小
? ? ? ? list1.next = mergeTwoListsDigui(list1.next, list2); // 下一個(gè)元素是:自己的下一個(gè)和大的元素比較
? ? ? ? return list1;
? ? }
}
第三類: 翻轉(zhuǎn)類題型-------遞歸
最基礎(chǔ)最常在面試中遇到的提醒,206一定要熟練掌握
/***
* 遞歸的方式反轉(zhuǎn)鏈表
* 1.遞歸依次返回的數(shù)據(jù)
* 2。下下個(gè)指向自己
* @param head
* @return
*/
public ListNodereverseListDigui(ListNode head) {
if (head ==null || head.next ==null) {
return head;
? ? }
ListNode res = reverseListDigui(head.next); // res=5? head=4
? ? head.next.next = head; // 4->5->null變成4->5->4
? ? head.next =null;// 4->null
? ? return res;
}
翻轉(zhuǎn)部分單鏈表---92(用到206題)
public ListNodereverseBetween(ListNode head, int left, int right) {
ListNode dummyNode =new ListNode(-1);
? ? dummyNode.next = head;
? ? ListNode pre = dummyNode;
? ? for (int i =0; i < left -1; i++) {
pre = pre.next;
? ? }
ListNode rightNode = pre;
? ? for (int i =0; i < right - left +1; i++) {
rightNode = rightNode.next;
? ? }
ListNode leftNode = pre.next;
? ? ListNode curr = rightNode.next;
? ? pre.next =null;
? ? rightNode.next =null;
? ? reverseLinkedList(leftNode);
? ? pre.next = rightNode;
? ? leftNode.next = curr;
? ? return dummyNode.next;
}
/***
* 206題,遞歸的方式反轉(zhuǎn)鏈表
* @param head
*/
public void reverseLinkedList(ListNode head) {
// 也可以使用遞歸反轉(zhuǎn)一個(gè)鏈表
? ? ListNode pre =null;
? ? ListNode cur = head;
? ? while (cur !=null) {
ListNode next = cur.next;
? ? ? ? cur.next = pre;
? ? ? ? pre = cur;
? ? ? ? cur = next;
? ? }
}
25:
public ListNodereverseKGroup(ListNode head, int k) {
ListNode dummy =new ListNode(0);
? ? dummy.next = head;
? ? ListNode pre = dummy;
? ? ListNode end = dummy;
? ? while (end.next !=null) {
for (int i =0; i < k && end !=null; i++) end = end.next;
? ? ? ? if (end ==null)break;
? ? ? ? ListNode start = pre.next;
? ? ? ? ListNode next = end.next;
? ? ? ? end.next =null;
? ? ? ? pre.next = reverse(start);
? ? ? ? start.next = next;
? ? ? ? pre = start;
? ? ? ? end = pre;
? ? }
return dummy.next;
}
/**
* 206
* @param head
* @return
*/
private ListNodereverse(ListNode head) {
ListNode pre =null;
? ? ListNode curr = head;
? ? while (curr !=null) {
ListNode next = curr.next;
? ? ? ? curr.next = pre;
? ? ? ? pre = curr;
? ? ? ? curr = next;
? ? }
return pre;
}
第四類:??環(huán)形鏈表------快慢指針
例題:141 Linked List Cycle 【easy】
例題:142 Linked List Cycle 【easy】
public ListNodedetectCycle(ListNode head) {
if (head ==null || head.next ==null) {
return null;
? ? }
ListNode fast = head, slow = head;
? ? // step1: 快慢指針一起走,指針碰頭快回頭
? ? while (fast !=null && fast.next !=null) {
fast = fast.next.next;
? ? ? ? slow = slow.next;
? ? ? ? if (fast == slow) {
break;
? ? ? ? }
}
if (fast ==null || fast.next ==null) {
return null;
? ? }
fast = head;
? ? // step2:一步一步走,再相遇就是goal
? ? while (fast != slow) {
fast = fast.next;
? ? ? ? slow = slow.next;
? ? }
return fast;
}
相交鏈表: 相交鏈表(160)
public ListNodegetIntersectionNode(ListNode headA, ListNode headB) {
if (headA ==null || headB ==null) {
return null;
? ? }
ListNode pA = headA, pB = headB;
? ? while (pA != pB) {
pA = pA ==null ? headB : pA.next; // 寫法,pA = pA == null
? ? ? ? pB = pB ==null ? headA : pB.next;
? ? }
return pA;
}
第六類:排序鏈表
143
public void reorderList(ListNode head) {
if (head ==null) {
return;
? ? }
ListNode mid = middleNode(head);
? ? ListNode l1 = head;
? ? ListNode l2 = mid.next;
? ? mid.next =null;
? ? l2 = reverseList(l2);
? ? mergeList(l1, l2);
}
public ListNodemiddleNode(ListNode head) {
ListNode slow = head;
? ? ListNode fast = head;
? ? while (fast.next !=null && fast.next.next !=null) {
slow = slow.next;
? ? ? ? fast = fast.next.next;
? ? }
return slow;
}
public ListNodereverseList(ListNode head) {
ListNode prev =null;
? ? ListNode curr = head;
? ? while (curr !=null) {
ListNode nextTemp = curr.next;
? ? ? ? curr.next = prev;
? ? ? ? prev = curr;
? ? ? ? curr = nextTemp;
? ? }
return prev;
}
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1_tmp;
? ? ListNode l2_tmp;
? ? while (l1 !=null && l2 !=null) {
l1_tmp = l1.next;
? ? ? ? l2_tmp = l2.next;
? ? ? ? l1.next = l2;
? ? ? ? l1 = l1_tmp;
? ? ? ? l2.next = l1;
? ? ? ? l2 = l2_tmp;
? ? }
}
148:(用到21題)
public ListNodesortList(ListNode head) {
return sortList(head, null);
}
private ListNodesortList(ListNode head, ListNode tail) {
if (head ==null) {
return head;
? ? }
if (head.next == tail) {
head.next =null;
? ? ? ? return head;
? ? }
ListNode slow = head, fast = head;
? ? while (fast != tail) {
slow = slow.next;
? ? ? ? fast = fast.next;
? ? ? ? if (fast != tail) {
fast = fast.next;
? ? ? ? }
}
ListNode mid = slow;
? ? ListNode listNode1 = sortList(head, mid);
? ? ListNode listNode2 = sortList(mid, tail);
? ? ListNode sorted = merge(listNode1, listNode2);
? ? return sorted;
}
private ListNodemerge(ListNode list1, ListNode list2) {
if (list1 ==null) {
return list2;
? ? }
if (list2 ==null)return list1;
? ? if (list1.val > list2.val) {// l2小
? ? ? ? list2.next = merge(list2.next, list1); // 下一個(gè)元素是:自己的下一個(gè)和大的元素比較
? ? ? ? return list2;
? ? }else {// l1 小
? ? ? ? list1.next = merge(list1.next, list2); // 下一個(gè)元素是:自己的下一個(gè)和大的元素比較
? ? ? ? return list1;
? ? }
}
回文鏈表: 234
public boolean isPalindrome(ListNode head) {
if (head ==null) {
return true;
? ? }
ListNode firstHalfEnd = endOfFirstHalf(head);
? ? ListNode secondHalfStart = reverseList(firstHalfEnd.next);
? ? ListNode p1 = head;
? ? ListNode p2 = secondHalfStart;
? ? boolean result =true;
? ? while (result && p2 !=null) {
if (p1.val != p2.val) {
result =false;
? ? ? ? }
p1 = p1.next;
? ? ? ? p2 = p2.next;
? ? }
firstHalfEnd.next = reverseList(secondHalfStart);
? ? return result;
}
private ListNodereverseList(ListNode head) {
ListNode prev =null;
? ? ListNode curr = head;
? ? while (curr !=null) {
ListNode nextTemp = curr.next;
? ? ? ? curr.next = prev; // 交換
? ? ? ? prev = curr;
? ? ? ? curr = nextTemp;
? ? }
return prev;
}
private ListNodeendOfFirstHalf(ListNode head) {
ListNode fast = head;
? ? ListNode slow = head;
? ? while (fast.next !=null && fast.next.next !=null) {
fast = fast.next.next;
? ? ? ? slow = slow.next;
? ? }
return slow;
}
第七類:拆分鏈表
例題:86 Partition List 【medium】
題意:給定一個(gè)鏈表以及一個(gè)目標(biāo)值,把小于該目標(biāo)值的所有節(jié)點(diǎn)都移至鏈表的前端,大于或等于目標(biāo)值的節(jié)點(diǎn)移至鏈表的尾端,同時(shí)要保持這兩部分在原先鏈表中的相對(duì)位置。
test case:
解題思路:?二分法。設(shè)置兩個(gè)指針left和right,順序遍歷整條鏈表,left、mid、target三者比較,根據(jù)情況left右移或者right左移。關(guān)鍵就在于邊界情況和元素有重復(fù)。
當(dāng) nums[mid] = nums[left] 時(shí),這時(shí)由于很難判斷 target 會(huì)落在哪,那么只能采取 left++
當(dāng) nums[mid] > nums[left] 時(shí),這時(shí)可以分為兩種情況,判斷左半部比較簡(jiǎn)單
當(dāng) nums[mid] < nums[left] 時(shí),這時(shí)可以分為兩種情況,判斷右半部比較簡(jiǎn)單
code
鏈表:(單鏈表:反轉(zhuǎn)、插入、刪除)
基礎(chǔ)知識(shí):鏈表如何實(shí)現(xiàn),如何遍歷鏈表。鏈表可以保證頭部尾部插入刪除操作都是O(1),查找任意元素位置O(N)
基礎(chǔ)題目:
Leetcode 206. Reverse Linked List
Leetcode 876. Middle of the Linked List
注意:快慢指針和鏈表反轉(zhuǎn)幾乎是所有鏈表類問題的基礎(chǔ),尤其是反轉(zhuǎn)鏈表,代碼很短,建議直接背熟。
第8類:鏈表相加求和
題目: 假設(shè)鏈表中每一個(gè)節(jié)點(diǎn)的值都在 0-9 之間,那么鏈表整體可以代表一個(gè)整數(shù)。 例如: 9->3->7 可以代表 937 給定兩個(gè)這樣的鏈表,頭節(jié)點(diǎn)為 head1 head2 生成鏈表相加的新鏈表。 如 9->3->7 和 6 -> 3 生成的新鏈表應(yīng)為 1 -> 0 -> 0 -> 0
總結(jié)鏈表: 基本操作:反轉(zhuǎn)鏈表,合并一個(gè)鏈表,找鏈表的中間節(jié)點(diǎn). 刪除一個(gè)節(jié)點(diǎn),添加一個(gè)節(jié)點(diǎn).
采用的方法:遞歸,快慢指針,迭代法
如何判斷一個(gè)單鏈表有環(huán)?鏈表翻轉(zhuǎn);
鏈表每 k 位逆序;
K個(gè)一組反轉(zhuǎn)鏈表(重點(diǎn))
鏈表反轉(zhuǎn)
算法題:兩個(gè)有序鏈表合并
6.算法題,鏈表求和
鏈表表示一個(gè)數(shù)字,求兩個(gè)數(shù)字相加之和,返回一個(gè)鏈表
算法題:給定一個(gè)鏈表L1、L2,每個(gè)元素是為10以內(nèi)的正整數(shù),鏈表表示一個(gè)數(shù)字,表頭為高位。 求兩個(gè)鏈表之差,以鏈表形式返回
給定兩個(gè)鏈表,存儲(chǔ)著兩個(gè)16進(jìn)制數(shù),鏈表的一個(gè)節(jié)點(diǎn)存儲(chǔ)著16進(jìn)制數(shù)的其中一個(gè)數(shù),從高位到低位,求相加的值,返回一個(gè)鏈表,鏈表中保存相加的結(jié)果。(先反轉(zhuǎn)鏈表,然后逐位相加,記錄進(jìn)位值,再與高位相加)手寫代碼
判斷單鏈表相交,找出節(jié)點(diǎn),手寫代碼
算法題:查找單鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn)
從長(zhǎng)序列中找出前K大的數(shù)字
算法題:冒泡排序的鏈表實(shí)現(xiàn)
鏈表逆序(頭條幾乎是必考的)
public ListNode reverseList(ListNode head)? ? {? ? ? ? if (head== null)? ? ? ? {? ? ? ? ? ? return null;}? ? ? ? if (head.next== null)? ? ? ? {? ? ? ? ? ? return head;}? ? ? ? ListNodeprev= null;ListNodecurrent= head;while (current != null)? ? ? ? {? ? ? ? ? ? ListNodenext= current.next;current.next= prev;prev= current;current= next;}? ? ? ? return prev;}復(fù)制代碼
刪除排序數(shù)組中的重復(fù)項(xiàng)
public int removeDuplicates(int[]nums)? ? {? ? ? ? intlength= nums.length;if (length==0|| length ==1)? ? ? ? {? ? ? ? ? ? return length;}? ? ? ? intsize=1;intpre= nums[0];for (inti=1; i < length; ){? ? ? ? ? ? if (nums[i]== pre)? ? ? ? ? ? {? ? ? ? ? ? ? ? i++;} else? ? ? ? ? ? {pre= nums[size++] = nums[i++];}? ? ? ? }? ? ? ? return size;}復(fù)制代碼
數(shù)組中找到重復(fù)元素
n個(gè)長(zhǎng)為n的有序數(shù)組,求最大的n個(gè)數(shù)
用O(1)的時(shí)間復(fù)雜度刪除單鏈表中的某個(gè)節(jié)點(diǎn) 把后一個(gè)元素賦值給待刪除節(jié)點(diǎn),這樣也就相當(dāng)于是刪除了當(dāng)前元素,只有刪除最后一個(gè)元素的時(shí)間為o(N)平均時(shí)間復(fù)雜度仍然為O(1)
public void deleteNode(ListNode node) {? ? ? ? ? ListNodenext= node.next;node.val= next.val;node.next= next.next;}復(fù)制代碼
刪除單鏈表的倒數(shù)第N個(gè)元素 兩個(gè)指針,第一個(gè)先走N步第二個(gè)再走,時(shí)間復(fù)雜度為O(N),參考link
public ListNode removeNthFromEnd(ListNode head, int n) {? ? ? ? ? if (head== null)? ? ? ? ? {? ? ? ? ? ? ? return null;}? ? ? ? ? if (head.next== null)? ? ? ? ? {? ? ? ? ? ? ? returnn==1? null : head;}? ? ? ? ? intsize=0;ListNodepoint= head;ListNodenode= head;do? ? ? ? ? {? ? ? ? ? ? ? if (size >= n + 1)? ? ? ? ? ? ? {point= point.next;}node= node.next;size++;} while (node != null);if (size== n)? ? ? ? ? {? ? ? ? ? ? ? return head.next;}node= point.next;point.next= node == null ? null : node.next;return head;}復(fù)制代碼
從長(zhǎng)序列中找出前K大的數(shù)字
用數(shù)組實(shí)現(xiàn)雙頭棧
publicstaticclass Stack<T>{? ? ? ? ? ? ? ? ? ? public Stack(intcap)? ? ? ? ? {? ? ? ? ? ? ? if (cap<=0)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? thrownewIllegalArgumentException();? ? ? ? ? ? ? }array=newObject[cap];left=0;right=cap-1;? ? ? ? ? }? ? ? ? ? ? private Object[]array;? ? ? ? ? privateintleft;? ? ? ? ? privateintright;? ? ? ? ? ? ? ? ? ? public void push1(T val)? ? ? ? ? {intindex=left+1;? ? ? ? ? ? ? if (index<right)? ? ? ? ? ? ? {array[index]=val;? ? ? ? ? ? ? }left=index;? ? ? ? ? }@SuppressWarnings("unchecked")? ? ? ? ? public T pop1()? ? ? ? ? {? ? ? ? ? ? ? if (left>0)? ? ? ? ? ? ? {return(T)array[left--];}returnnull;? ? ? ? ? }? ? ? ? ? ? ? ? ? ? public void push2(T val)? ? ? ? ? {intindex=right-1;? ? ? ? ? ? ? if (index>left)? ? ? ? ? ? ? {array[index]=val;? ? ? ? ? ? ? }right=index;? ? ? ? ? }@SuppressWarnings("unchecked")? ? ? ? ? public T pop2()? ? ? ? ? {? ? ? ? ? ? ? if (right<array.length)? ? ? ? ? ? ? {return(T)array[right++];? ? ? ? ? ? ? }returnnull;? ? ? ? ? }? ? ? }復(fù)制代碼
兩個(gè)鏈表求和,返回結(jié)果也用鏈表表示 1 -> 2 -> 3 + 2 -> 3 -> 4 = 3 -> 5 -> 7
public ListNode addTwoNumbers(ListNode node1, ListNode node2)? ? ? {? ? ? ? ? ListNodehead= null;ListNodetail= null;booleanupAdd=false;while (!(node1== null && node2 == null))? ? ? ? ? {? ? ? ? ? ? ? ListNodemidResult= null;if (node1 != null)? ? ? ? ? ? ? {midResult= node1;node1= node1.next;}? ? ? ? ? ? ? if (node2 != null)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? if (midResult== null)? ? ? ? ? ? ? ? ? {midResult= node2;} else? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? midResult.val += node2.val;}node2= node2.next;}? ? ? ? ? ? ? if (upAdd)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? midResult.val += 1;}? ? ? ? ? ? ? if (midResult.val >= 10)? ? ? ? ? ? ? {upAdd=true;midResult.val %= 10;}? ? ? ? ? ? ? else? ? ? ? ? ? ? {upAdd=false;}? ? ? ? ? ? ? if (head== null)? ? ? ? ? ? ? {head= midResult;tail= midResult;} else? ? ? ? ? ? ? {tail.next= midResult;tail= midResult;}? ? ? ? ? }? ? ? ? ? if (upAdd)? ? ? ? ? {tail.next= new ListNode(1);}? ? ? ? ? return head;}復(fù)制代碼
交換鏈表兩兩節(jié)點(diǎn)
public ListNode swapPairs(ListNode head)? ? ? {? ? ? ? ? if (head== null)? ? ? ? ? {? ? ? ? ? ? ? return null;}? ? ? ? ? if (head.next== null)? ? ? ? ? {? ? ? ? ? ? ? return head;}? ? ? ? ? ListNodecurrent= head;ListNodeafter= current.next;ListNode nextCurrent;head= after;do? ? ? ? ? {nextCurrent= after.next;after.next= current;if (nextCurrent== null)? ? ? ? ? ? ? {current.next= null;break;}current.next= nextCurrent.next;after= nextCurrent.next;if (after== null)? ? ? ? ? ? ? {current.next= nextCurrent;break;}current= nextCurrent;} while (true);return head;}復(fù)制代碼
找出數(shù)組中和為給定值的兩個(gè)元素,如:[1, 2, 3, 4, 5]中找出和為6的兩個(gè)元素。
public int[]twoSum(int[]mun,int target)? ? ? {? ? ? ? ? Maptable= new HashMap<>();for (inti=0; i < mun.length; ++i){? ? ? ? ? ? ? Integervalue= table.get(target - mun[i]);if (value != null)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? return new int[]{i, value};}? ? ? ? ? ? ? table.put(mun[i], i);}? ? ? ? ? return null;}
單鏈表的基本操作(增刪改查)
獲取單鏈表的長(zhǎng)度
public int getLength(Node head){
if(head ==null){
return 0;
? ? }
int len =0;
? ? while(head !=null){
len++;
? ? ? ? head = head.next;
? ? }
return len;
}
查詢指定索引的節(jié)點(diǎn)值或指定值得節(jié)點(diǎn)值的索引
/** 獲取指定角標(biāo)的節(jié)點(diǎn)值 */
public int getValueOfIndex(Node head, int index)throws Exception {
if (index <0 || index >= getLength(head)) {
throw new Exception("角標(biāo)越界!");
? ? }
if (head ==null) {
throw new Exception("當(dāng)前鏈表為空!");
? ? }
Node dummyHead = head;
? ? while (dummyHead.next !=null && index >0) {
dummyHead = dummyHead.next;
? ? ? ? index--;
? ? }
return dummyHead.value;
}
/** 獲取節(jié)點(diǎn)值等于 value 的第一個(gè)元素角標(biāo) */
public int getNodeIndex(Node head, int value) {
int index = -1;
? ? Node dummyHead = head;
? ? while (dummyHead !=null) {
index++;
? ? ? ? if (dummyHead.value == value) {
return index;
? ? ? ? }
dummyHead = dummyHead.next;
? ? }
return -1;
}
鏈表添加一個(gè)元素
頭部添加一個(gè)元素
public NodeaddAtHead(Node head, int value){
Node newHead =new Node(value);
? ? newHead.next = head;
? ? return newHead;
}
尾部添加一個(gè)元素
public void addAtTail(Node head, int value){
Node node =new Node(value);
? ? Node dummyHead = head;
? ? //找到未節(jié)點(diǎn) 注意這里是當(dāng)元素的下一個(gè)元素為空的時(shí)候這個(gè)節(jié)點(diǎn)即為未節(jié)點(diǎn)
? ? while( dummyHead.next !=null){
dummyHead = dummyHead.next;
? ? }
dummyHead.next = node;
}
鏈表刪除一個(gè)元素
由單鏈表的增加刪除可以看出,鏈表的想要對(duì)指定索引進(jìn)行操作(增加,刪除),的時(shí)候必須獲取該索引的前一個(gè)元素。記住這句話,對(duì)鏈表算法題很有用。
public NodedeleteElement(Node head, int index)throws Exception {
int size = getLength(head);
? ? if (index <0 || index >= size) {
throw new Exception("角標(biāo)越界!");
? ? }
if (index ==0) {
return deleteHead(head);
? ? }else if (index == size -1) {
deleteTail(head);
? ? }else {
Node pre = head;
? ? ? ? while (pre.next !=null && index >1) {
pre = pre.next;
? ? ? ? ? ? index--;
? ? ? ? }
//循環(huán)結(jié)束后 pre 保存的是索引的上一個(gè)節(jié)點(diǎn) 將其指向索引的下一個(gè)元素
? ? ? ? if (pre.next !=null) {
pre.next = pre.next.next;
? ? ? ? }
}
return head;
}
尋找單鏈表的中間元素:快慢指針? (類似于判讀一個(gè)鏈表是否有環(huán))----題目(143)歸并排序
已知一個(gè)單鏈表求倒數(shù)第 N 個(gè)節(jié)點(diǎn)()
常用技巧
1.使用dummy node。dummy node就是在鏈表的head前加一個(gè)節(jié)點(diǎn)指向head,即dummy->head,可以理解成一個(gè)虛擬節(jié)點(diǎn)。多針對(duì)于單鏈表沒有前向指針的問題,保證鏈表的head不會(huì)在刪除操作中丟失。通常情況下,如果鏈表的head會(huì)發(fā)生變化,譬如刪除或者被修改等,可以創(chuàng)建dummy node:
ListNodedummy=newListNode(0);dummy.next = head;復(fù)制代碼
這樣就使得操作head節(jié)點(diǎn)與操作其他節(jié)點(diǎn)沒有區(qū)別。
2.雙指針法。對(duì)于尋找鏈表的某個(gè)特定位置,或者判斷是否有環(huán)等問題時(shí),可以用兩個(gè)指針變量fast和slow:
ListNodeslow=head;ListNodefast=head;復(fù)制代碼
以不同的速度遍歷該鏈表,以找到目標(biāo)位置。注意:在測(cè)試時(shí),需要分別選取鏈表長(zhǎng)度為奇數(shù)和偶數(shù)的test case,可以驗(yàn)證算法在一般情況下的正確性避免遺漏。
3.交換節(jié)點(diǎn)的處理。如果需要交換兩個(gè)節(jié)點(diǎn)的位置,譬如24題?Swap Nodes in Pairs,需要交換兩個(gè)相鄰位置的節(jié)點(diǎn),對(duì)于這兩個(gè)前驅(qū)節(jié)點(diǎn),他們的next指針會(huì)受到影響,這兩個(gè)節(jié)點(diǎn)本身也會(huì)受到影響,可以用以下步驟:
1)先交換兩個(gè)前驅(qū)節(jié)點(diǎn)的next指針的值
2)再交換這兩個(gè)節(jié)點(diǎn)的next指針的值
無論這兩個(gè)節(jié)點(diǎn)的相對(duì)位置和絕對(duì)位置如何,以上的處理方式均可成立
4.同時(shí)操作兩個(gè)鏈表的處理。遇到這種題目,循環(huán)的條件一般可以用while(list1 && list2),當(dāng)循環(huán)跳出來后,再處理剩下非空的鏈表,這相當(dāng)于:邊界情況特殊處理,常規(guī)情況常規(guī)處理
鏈表的問題是面試當(dāng)中常常會(huì)問到的,比如鏈表的倒置,刪除鏈表中某個(gè)結(jié)點(diǎn),合并兩個(gè)排序鏈表,合并 k 個(gè)排序鏈表,排序兩個(gè)無序鏈表等。
這些題目一般有一些相應(yīng)的技巧可以解決。
第一,引入哨兵結(jié)點(diǎn)。如我們開頭說的 dummy node 結(jié)點(diǎn)。在任何時(shí)候,不管鏈表是不是空,head結(jié)點(diǎn)都會(huì)一直指向這個(gè)哨兵結(jié)點(diǎn)。我們也把這種有哨兵結(jié)點(diǎn)的鏈表叫做帶頭鏈表。
第二,雙指針法。這種用法適用于查找鏈表中某個(gè)位置,判斷鏈表是否有環(huán)等
第三,分之歸并法。這種用法適用于鏈表的排序處理,如合并 k 個(gè)排序鏈表,排序兩個(gè)無序鏈表等。
第四,在解答的過程中,要多考慮邊界情況。
鏈表為空
鏈表中只有一個(gè)結(jié)點(diǎn)
鏈表中只包含兩個(gè)結(jié)點(diǎn)
代碼在處理頭結(jié)點(diǎn)跟尾結(jié)點(diǎn)是否存在什么問題
鏈表的相關(guān)重要的操作:
1.dummo:添加頭節(jié)點(diǎn)。 因?yàn)闀?huì)移動(dòng)鏈表。所以要有一個(gè)保存最開始的
2.pr-->node
3.next-->node??
如何移動(dòng)指針
特點(diǎn):沒有指向前面的指針,所以需要定義一個(gè)pre
最后返回dummy .next()。就是鏈表
鏈表的一些操作:
1.如何遍歷一個(gè)鏈表
while (head !=null) {
head=head.next;
}
2.如何指向下一個(gè)指針
head=head.next;區(qū)別:
// head.next = head.next.next;
// head=head.next;
鏈表專項(xiàng)突破