連結串列題彙總
連結串列和演算法簡直不是一個東西,縱然做了很多演算法題,沒有專門訓練過連結串列是很容易成為炮灰的,用幾天時間把力扣上的連結串列題刷了記錄一下。
主要操作有快慢指標、連結串列合併、拆分連結串列、重組連結串列、連結串列移位、連結串列成環、連結串列相交、連結串列反轉、連結串列排序等,一般都是指標指來指去就夠了,儘量不要用陣列儲存,否則體現不出水平,時常需要判空。
以下題目大致按難度排序
思路:1.常規的遍歷求長度,再跑到中點停下來 2.快慢指標,一個走一步,一個走兩步,快指標跑到末尾時慢指標就在中點
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * }力扣876*/ class Solution { public ListNode middleNode(ListNode head) { ListNode fast=head,slow=head; while(fast!=null && fast.next!=null){ slow=slow.next; fast=fast.next.next; } return slow; } }
思路:遍歷過程中,下一個節點需要刪除的話,則把當前節點的指標指向下下個節點。特判頭節點。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode deleteNode(ListNode head, int val) { if(head==null) return null; ListNode cur=head;劍指offer18//特判第一個節點 if(head.val==val) return head.next; while(cur!=null){ //下一個節點要刪除,就直接指向 下下個節點,退出 if(cur.next.val==val){ cur.next=cur.next.next; break; } cur=cur.next; } return head; } }
思路1:計算連結串列長度差k,長的先跑k步,短的再開始跑,會在交點相遇,若無交點,最後同時走到null。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if(headA==headB) return headA; int len1=size(headA),len2=size(headB); //保證A是短的,B是長的 if(len1>len2){ ListNode temp=headA; headA=headB; headB=temp; } int k=Math.abs(len1-len2); ListNode cur1=headA,cur2=headB; //長的連結串列B先走k步 for(int i=0;i<k;i++){ cur2=cur2.next; } while(cur1!=cur2){ cur1=cur1.next; cur2=cur2.next; } return cur1; } //獲取連結串列長度 public static int size(ListNode head){ int res=0; while(head!=null){ res++; head=head.next; } return res; } }劍指offer52
思路2:一起跑,跑完換線跑,有交點會相遇,沒交點最後也都是跑完全程同時到null,不會死迴圈。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode cur1=headA,cur2=headB; while(cur1!=cur2){ cur1=cur1==null?headB:cur1.next; cur2=cur2==null?headA:cur2.next; } return cur1; } }劍指offer52
力扣141:環形連結串列(騰訊面試)
思路:快慢指標,如果有環的話,不會遇到null;一快一慢跑進環裡總會相遇,一旦遇到null就可以退出。注意判空避免空指標異常。
/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { if(head==null) return false; ListNode cur1=head,cur2=head.next; while(cur1!=null && cur2!=null){ if(cur1==cur2){ return true; } cur1=cur1.next; if(cur2.next!=null) cur2=cur2.next.next; else break; } return false; } }力扣141
思路:逆序儲存,從首位開始往後走,注意三個點,進位、一條連結串列先為空、走完全程還有進位。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode cur1=l1,cur2=l2,ans=new ListNode(0),res=ans,last=ans; int x=0,sum=0;//進位 while( cur1!=null || cur2!=null || x!=0){ sum=x; if(cur1!=null && cur2!=null){ sum=cur1.val+cur2.val+x; cur1=cur1.next; cur2=cur2.next; }else if(cur1!=null && cur2==null){ sum=cur1.val+x; cur1=cur1.next; }else if(cur1==null && cur2!=null){ sum=cur2.val+x; cur2=cur2.next; } x=sum/10; last=ans; ans.val=sum%10; ans.next=new ListNode(0); ans=ans.next; } last.next=null; return res; } }面試題02.05
思路:移動數位對長度求模,看移位把連結串列拆成兩條,記得斷開第一條連結串列末尾,尾再接上頭。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode rotateRight(ListNode head, int k) { if(head==null) return null; int len=size(head); k=k%len; if(k==0 || head.next==null) return head; ListNode cur=head; for(int i=1;i<(len-k);i++){ cur=cur.next; } ListNode temp=cur.next;//後面部分連結串列起點,不會是null cur.next=null;//前面部分末尾置空 cur=temp; //找到末尾節點 while(cur.next!=null){ cur=cur.next; } //末尾節點->頭節點 cur.next=head; return temp; } public static int size(ListNode head){ int res=0; ListNode cur=head; while(cur!=null){ res++; cur=cur.next; } return res; } }力扣61
思路:標記一下G中的數,遍歷一次連結串列,對連續存在於G的數區間計數,累計到答案中。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public int numComponents(ListNode head, int[] G) { //最終答案 連續存在於G的數的數量 int ans=0,num=0; //對G中的數做一下標記 HashMap<Integer,Integer> map=new HashMap<>(); for(int i=0;i<G.length;i++) map.put(G[i],1); ListNode cur=head; while(cur!=null){ if( map.get(cur.val)!=null ){ num++; }else { if(num!=0) ans++; num=0; } cur=cur.next; } if(num!=0) ans++; return ans; } }力扣817
思路:從頭走到尾,將當前結點指向上一個結點。
實現:上一個結點用last記錄,當前結點用cur記錄。下一個結點用t臨時儲存,因為修改了當前結點的指向就失去了下一個結點的位置,需要儲存。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseList(ListNode head) { if(head==null) return null; ListNode cur=head; ListNode t,last=null; while(cur != null){//當前不為空,把當前指向上一個 t=cur.next;//臨時儲存下一個結點 cur.next=last;//當前結點指向上一個結點 last=cur;//更新下一輪的【上一個結點】為【當前結點】 if(t==null) break; cur=t;//更新下一輪的【當前結點】為【下一個結點】 } return cur; } }劍指offer24
思路:將連結串列分為3段,第1段正常,第2段反轉,第3段正常。注意第1段和第3段為空的情況。詳看程式碼,附圖解。
class Solution { public ListNode reverseBetween(ListNode head, int m, int n) { if(head==null || head.next==null) return head; ListNode temp=new ListNode(-1),cur=head; //第1段為空 則 直接返回第2第3段連結串列 if(m==1) return reverse(head,n-m+1); temp.next=head; int i=1; //止步於 第2段連結串列前 while(i<(m-1)){ cur=cur.next; i++; } cur.next=reverse(cur.next,n-m+1); return temp.next; } public static ListNode reverse(ListNode head,int x){ ListNode tail=head,cur=head,last=null,temp; while(x-->0){ temp=cur.next;//臨時儲存下一個節點 cur.next=last;//當前節點指向上一個節點 last=cur;//更新上一個節點,準備下一輪 cur=temp;//移動當前節點 } /* 第3段連結串列不為空 tail last cur null ← 0 ← 0 ← 0 0 → 0 → 0 第3段連結串列為空 tail last cur null ← 0 ← 0 ← 0 null */ while(cur!=null){ tail.next=cur; tail=tail.next; cur=cur.next; } return last; } }力扣92
題意:連結串列多出一個隨即指標隨機指向,要求複製出一模一樣的連結串列。
思路:主流解法都是在原連結串列中插入複製連結串列,最後拆分連結串列。需要保持原連結串列的記憶體地址不變,而不是單單分離出複製連結串列。
/* // Definition for a Node. class Node { int val; Node next; Node random; public Node(int val) { this.val = val; this.next = null; this.random = null; } } */ class Solution { public Node copyRandomList(Node head) { if(head==null) return null; //1.原連結串列的每個節點後插入複製節點 Node cur=head;// while(cur!=null){ //複製出當前節點 Node temp=new Node(cur.val); //複製點插入cur和cur.next之間 temp.next=cur.next; cur.next=temp; //移動當前節點 cur=temp.next; } //2.處理random指標 cur=head; while(cur!=null){ if( cur.random!=null ) cur.next.random=cur.random.next;//複製點->random的複製點 cur=cur.next.next; } //3.分離連結串列 cur=head.next;//第一個複製點 Node res=head.next,pre=head; while(cur!=null){ pre.next=cur.next; pre=cur.next; if(pre==null){//特判末尾節點 cur.next=null; break; } cur.next=pre.next; cur=pre.next; } return res; } }劍指offer35
力扣143:重排連結串列(招銀面試)
題意:L0→L1→L2→L3→L4...→Ln-1→Ln 變成 L0→Ln→L1→Ln-1→L2→Ln-2...
思路:1.陣列儲存再雙指標從新組成 2.快慢指標找中點,拆分成兩條連結串列,第二條反轉,再合併;融合幾個簡單操作。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public void reorderList(ListNode head) { if(head==null || head.next==null) return; ListNode mid=mid(head); ListNode cur=head; //斷開連結串列 while(cur!=null){ if(cur.next==mid) cur.next=null; cur=cur.next; } mid=reverse(mid);//反轉後半部分連結串列 merge(head,mid); } //獲取連結串列中點 public static ListNode mid(ListNode head){ //快慢指標 ListNode fast=head,slow=head; while(fast!=null && fast.next!=null){ slow=slow.next; fast=fast.next.next; } return slow; } //反轉連結串列 public static ListNode reverse(ListNode head){ if(head==null) return null; ListNode cur=head,t,last=null; while(cur != null){//當前不為空,把當前指向上一個 t=cur.next;//臨時儲存下一個結點 cur.next=last;//當前結點指向上一個結點 last=cur;//更新下一輪的【上一個結點】為【當前結點】 if(t==null) break; cur=t;//更新下一輪的【當前結點】為【下一個結點】 } return cur; } //交替合併連結串列 public static ListNode merge(ListNode l1,ListNode l2){ ListNode cur1=l1,cur2=l2,temp1,temp2; while(cur1!=null && cur2!=null){ temp1=cur1.next; cur1.next=cur2; temp2=cur2.next; //l1.size()<=l2.size(),防止丟失l2最後一個 if(temp1==null) break; cur2.next=temp1; cur1=temp1; cur2=temp2; } cur1.next=cur2; return l1; } }力扣143
思路:樣例看了很久才看明白什麼意思,小於x的放前面,大於等於x的放後面,在初始連結串列中的相對位置不變。小於x的節點按順序放在一條連結串列裡,大於等於x的節點按順序放在另一條連結串列裡,最後把兩條連結串列接起來。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode partition(ListNode head, int x) { ListNode l1=new ListNode(-1),l2=new ListNode(-1),cur=head; ListNode res1=l1,res2=l2; while(cur!=null){ if(cur.val<x){ l1.next=cur; l1=l1.next; }else{ l2.next=cur; l2=l2.next; } cur=cur.next; } l2.next=null; l1.next=res2.next; return res1.next; } }力扣86
思路:均分長度取整,多出的節點分給前面的連結串列,人均一個。截斷連結串列時注意記錄下一條連結串列的起點,節點若不夠分則後面的連結串列為空,需要判空。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode[] splitListToParts(ListNode root, int k) { ListNode[] ans=new ListNode[k]; if(root==null) return ans; int len=0; ListNode cur=root; while(cur!=null){ len++; cur=cur.next; } //均分之後的餘數,註定前r個連結串列多1個節點,x是均分節點數 int r=len%k,x=len/k; //第i個連結串列 當前連結串列長度 int i=0,num=0; cur=root; ListNode start,temp=root; //獲取完最後一個連結串列沒有對cur置空,需要用i<k退出 while( cur!=null && i<k){ //r個較長的連結串列不會必定遇到空的情況 if(i<r){ start=temp; cur=start; for(int j=1;j<(1+x);j++){ cur=cur.next; } temp=cur.next;//儲存下一個起點 cur.next=null;//截斷連結串列尾 ans[i]=start;//儲存每段連結串列頭 }else{ start=temp; cur=start; for(int j=1;j<x && cur!=null;j++) cur=cur.next; //若遇到空說明連結串列已經被拆完了,直接退出 if(cur==null){ ans[i]=start; break; } temp=cur.next; cur.next=null; ans[i]=start; } i++; } return ans; } }力扣725
思路:歸併排序,找中點分割+合併。空間複雜度不符合題目要求。但就是不想看迭代,遇到了算我倒黴。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode sortList(ListNode head) { return segment(head); } //歸併排序,分割一條連結串列為兩個連結串列再合併成一條連結串列 public static ListNode segment(ListNode head){ if(head==null) return null; if(head.next==null) return head; //快慢指標找中點 ListNode slow=head,fast=head.next; while(fast!=null && fast.next!=null){ slow=slow.next; fast=fast.next.next; } ListNode l2=segment(slow.next); slow.next=null;//可能並非均分,將就著斷 ListNode l1=segment(head); return merge(l1,l2); } //合併有序連結串列 public static ListNode merge(ListNode l1,ListNode l2){ ListNode res=new ListNode(0); ListNode cur1=l1,cur2=l2,now=res; while(cur1!=null && cur2!=null){ if(cur1.val<cur2.val){ now.next=cur1; cur1=cur1.next; }else{ now.next=cur2; cur2=cur2.next; } now=now.next; } //誰沒跑完就把它接到尾巴 now.next=cur2==null?cur1:cur2; return res.next; } }力扣148
思路:找中點分割,第二條反轉,一起從頭遍歷看值是否相同。注意空連結串列返回true。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public boolean isPalindrome(ListNode head) { if(head==null || head.next==null) return true; ListNode mid=mid(head); ListNode cur=head; //斷開連結串列 while(cur!=null){ if(cur.next==mid) cur.next=null; cur=cur.next; } mid=reverse(mid); ListNode cur1=head,cur2=mid; //cur1.size()<=cur2.size() while(cur1!=null){ if(cur1.val!=cur2.val) return false; cur1=cur1.next; cur2=cur2.next; } return true; } //獲取連結串列中點 public static ListNode mid(ListNode head){ //快慢指標 ListNode fast=head,slow=head; while(fast!=null && fast.next!=null){ slow=slow.next; fast=fast.next.next; } return slow; } //反轉連結串列 public static ListNode reverse(ListNode head){ if(head==null) return null; ListNode cur=head,t,last=null; while(cur != null){//當前不為空,把當前指向上一個 t=cur.next;//臨時儲存下一個結點 cur.next=last;//當前結點指向上一個結點 last=cur;//更新下一輪的【上一個結點】為【當前結點】 if(t==null) break; cur=t;//更新下一輪的【當前結點】為【下一個結點】 } return cur; } }力扣234
思路:蓄水池抽樣演算法
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { /** @param head The linked list's head. Note that the head is guaranteed to be not null, so it contains at least one node. */ static ListNode head; public Solution(ListNode head) { this.head=head; } /** Returns a random node's value. */ public int getRandom() { int cnt=0,ans=0; Random r=new Random(); ListNode cur=head; while(cur!=null){ cnt++; if(r.nextInt()%cnt==0) ans=cur.val; cur=cur.next; } return ans; } } /** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(head); * int param_1 = obj.getRandom(); */力扣382
力扣328:奇偶連結串列(映客筆試)
思路:分別用兩條連結串列儲存奇偶節點,最後拼接,注意偶連結串列末尾置空,記錄偶連結串列頭。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode oddEvenList(ListNode head) { if(head==null || head.next==null) return head; ListNode cur1=head,cur2=head.next,cur=head.next.next; ListNode temp=head.next;//防止丟失偶數連結串列的頭部 int cnt=1; while(cur!=null){ if(cnt%2==1){//奇數 cur1.next=cur; cur1=cur1.next; }else{ cur2.next=cur; cur2=cur2.next; } cur=cur.next; cnt++; } cur1.next=temp; cur2.next=null; cur=head; return head; } }力扣328
思路:反轉,遍歷,累計val*2的冪次方。
class Solution { static int[] two=new int[30]; public int getDecimalValue(ListNode head) { if(head==null) return 0; two[0]=1; for(int i=1;i<30;i++) two[i]=two[i-1]*2; head=reverse(head); ListNode cur=head; int i=0,ans=0; while(cur!=null){ ans+=two[i++]*cur.val; cur=cur.next; } return ans; } //反轉連結串列 public static ListNode reverse(ListNode head){ if(head==null) return null; ListNode cur=head,t,last=null; while(cur != null){//當前不為空,把當前指向上一個 t=cur.next;//臨時儲存下一個結點 cur.next=last;//當前結點指向上一個結點 last=cur;//更新下一輪的【上一個結點】為【當前結點】 if(t==null) break; cur=t;//更新下一輪的【當前結點】為【下一個結點】 } return cur; } }力扣1290
思路:bfs過程中,每次把一層的節點弄成連結串列,先放在ArrayList裡,最後再用toArray轉陣列。
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode[] listOfDepth(TreeNode tree) { if(tree==null) return null; LinkedList<TreeNode> list=new LinkedList<>(); ArrayList<ListNode> res = new ArrayList<>(); list.add(tree); ListNode temp=new ListNode(-1),cur;//偽節點頭 while(list.size()>0){ cur=temp; //獲取當前層的節點數,拼接一條連結串列 int len=list.size(); for(int i=0;i<len;i++){ TreeNode now=list.poll(); cur.next=new ListNode(now.val); cur=cur.next; //略過空的 if(now.left!=null) list.add(now.left); if(now.right!=null) list.add(now.right); } res.add(temp.next); } return res.toArray(new ListNode[]{});//ArrayList轉陣列 } }面試題04.03
力扣23:合併K個升序連結串列(映客筆試)
思路1:優先佇列
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode mergeKLists(ListNode[] lists) { //優先佇列 Comparator<ListNode> comparator=new Comparator<ListNode>() { public int compare(ListNode o1, ListNode o2) { return o1.val<o2.val?-1:1; } }; Queue<ListNode> que=new PriorityQueue<ListNode>(comparator); ListNode temp,cur; for(int i=0;i<lists.length;i++){ cur=lists[i]; while(cur!=null){ que.add(cur); temp=cur; cur=cur.next; temp.next=null;//每個節點的next都置空,防止成環 } } ListNode head=new ListNode(-1); cur=head; while(que.size()>0){ cur.next=que.poll(); cur=cur.next; } return head.next; } }優先佇列
思路2:歸併排序
class Solution { public ListNode mergeKLists(ListNode[] lists) { if(lists.length==0) return null; return sort(0,lists.length-1,lists); } public static ListNode sort(int l,int r,ListNode[] lists){ if(l==r) return lists[l]; int mid=(l+r)/2; ListNode l1=sort(l,mid,lists); ListNode l2=sort(mid+1,r,lists); return merge(l1,l2); } //合併有序連結串列 public static ListNode merge(ListNode l1,ListNode l2){ ListNode res=new ListNode(0); ListNode cur1=l1,cur2=l2,now=res; while(cur1!=null && cur2!=null){ if(cur1.val<cur2.val){ now.next=cur1; cur1=cur1.next; }else{ now.next=cur2; cur2=cur2.next; } now=now.next; } //誰沒跑完就把它接到尾巴 now.next=cur2==null?cur1:cur2; return res.next; } }歸併排序
思路:遍歷過程中,維持前面部分有序,嚴格遞增。當前節點需要調整位置時,從頭遍歷找到適當的插入位置(前面小於當前節點,後面大於當前節點)。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode insertionSortList(ListNode head) { if(head==null || head.next==null) return head; //偽頭節點 ListNode dummy=new ListNode(0); dummy.next=head; //pre為cur的前一個節點,通過cur=pre.next去移動即可 ListNode pre=head,cur=head.next,temp; while(cur!=null){ if(pre.val<=cur.val){ pre=pre.next; }else{ temp=dummy; //找到最後一個位置比cur小的位置,保證遞增嚴格 while(temp.next.val<=cur.val){ temp=temp.next; } /* temp pre cur 1 → 2 → 4 → 8 → 5 → 6... */ pre.next=cur.next; cur.next=temp.next; temp.next=cur; /* temp cur pre 1 → 2 → 4 → 5 → 8 → 6... */ } cur=pre.next; } return dummy.next; } }力扣147
思路:對每一小段需要翻轉k個節點的子連結串列,需要子連結串列的頭節點,子連結串列的前驅節點,最後返回子連結串列的尾節點當作新的前驅節點。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode dummy=new ListNode(-1),cur=head; dummy.next=head; int len=0; while(cur!=null){ cur=cur.next; len++; } cur=dummy; int t=len/k;//反轉t段長度為k的連結串列 while(t-->0){ cur=reverseK(cur,cur.next,k); } cur=dummy.next; return dummy.next; } //對後續k個節點進行反轉 public static ListNode reverseK(ListNode pre,ListNode head,int k){ if(head==null) return head; //反轉時需要指向上一個節點,當前節點,臨時儲存下一個節點 ListNode last=null,cur=head,temp; while(k-->0 && cur!=null){ //整條鏈的前驅節點不斷往後指,改變連結串列指向 pre.next=cur; temp=cur.next; cur.next=last; last=pre.next; cur=temp; } //返回反轉後的連結串列尾,當作下k個節點連結串列的前驅節點 head.next=cur; return head; } }力扣25
寫在最後,Java用容器很容易解出這些題,例如用ArrayList存起來後,翻轉、找中點等操作用下標即可。筆試的時候可以騙分,實在不行寫一下極端情況(head==null || head.next==null)返回head大概率也能騙點分,悔不當初;面試手撕用容器就落了下乘。做題需要留意的有這些變數,頭節點head、偽頭節點(頭節點的前驅)dummy、遍歷到的當前節點cur、當前節點的上一個節點last、臨時儲存下一個節點temp(翻轉後無法通過cur=cur.next獲取,先用temp存一下),以及各種情況下判空break,儘量有屬於自己的命名風格以及修改操作習慣,看了題解也嘗試用自己的風格寫出來,在草稿紙上塗塗畫畫,以後遇到就不成問題了。別無他長,惟手熟爾。