关于链表的心得体会


1.反转链表

  经典考题,针对链表的反转,第一时间需要联想到将链表的指针进行反转,而这种一系列的变化,可以使用递归,也可以使用while

迭代

假设链表为 1 \rightarrow 2 \rightarrow 3 \rightarrow \varnothing1→2→3→∅,我们想要把它改成 \varnothing \leftarrow 1 \leftarrow 2 \leftarrow 3∅←1←2←3。

在遍历链表时,将当前节点的 \textit{next}next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

class Solution {
public ListNode reverseList(ListNode head) {

        //使用指针的方法对全部的值进行反转
        //首先定义空的节点
        ListNode pre = null;
        ListNode cur = head;
        while(cur!=null){
        ListNode next = cur.next;
        //当前节点的子节点将指向前节点
        cur.next = pre;
        //当前节点变为前节点
        pre = cur;
        //下一个节点变为当前节点
        cur = next;
        }
        return pre;
}
}

 

 

递归

递归版本稍微复杂一些,其关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?

假设链表为:

n1nk1nknk+1nm

若从节点 n_{k+1}nk+1 到 n_mnm 已经被反转,而我们正处于 n_knk
n1nk1nknk+1nm

我们希望 n_{k+1}nk+1 的下一个节点指向 n_knk

​所以,n_k.\textit{next}.\textit{next} = n_knk.next.next=nk
需要注意的是 n_1n1 的下一个节点必须指向 \varnothing∅。如果忽略了这一点,链表中可能会产生环

class Solution {
public ListNode reverseList(ListNode head) {
  if (head == null || head.next == null) {
    return head;
  }
  ListNode newHead = reverseList(head.next);
  head.next.next = head;
  head.next = null;
  return newHead;
  }
}

 

2.链表取中间节点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

 

需要联想到的方法:

1.双指针(快慢指针)法,既然是一个数组,我分别定义一个走两步的head和一个走一步的head,每当head1走两步,我的head2走1步,head1走完,head2刚好走到中间。

public ListNode middleNode(ListNode head) {
  ListNode slow = head, fast = head;
  while (fast != null && fast.next != null) {
  slow = slow.next;
  fast = fast.next.next;
  }
  return slow;
}

 

2.单指针法:我每走一步,i++,在第二次遍历是,我走到i/2步,结束

public ListNode middleNode(ListNode head) {
  int n = 0;
  ListNode cur = head;
  while (cur != null) {
  ++n;
  cur = cur.next;
}
int k = 0;
cur = head;
while (k < n / 2) {
++k;
cur = cur.next;
}
return cur;
}

  

 

3.数组法:将节点变为数组,并记录一个i++,变成节点数组之后直接获取最大i/2的数组内容,也就是中间的结点。

public ListNode middleNode(ListNode head) {
ListNode[] A = new ListNode[100];
int t = 0;
while (head != null) {
A[t++] = head;
head = head.next;
}
return A[t / 2];
}

 

3.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

 

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:

输入:l1 = [], l2 = []
输出:[]
示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

 

递归,思想是这样的,我先将l1和l2看作一个单独的结点,进行判断,如果L1小于L2,我就讲L1.next和L2的剩余内容又看作一个整体,再进行递归。

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null){
            return l2;
        }else if(l2 == null){
            return l1;
        }else if(l1.val<l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

 

 

4.将链表两两进行反转

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

 

输入:head = [1,2,3,4]
输出:[2,1,4,3]

 

递归,将前两个看作一个单位进行递归,返回

public ListNode swapPairs(ListNode head) {
        if(head==null||head.next==null){return head;}
        
        ListNode pre = head;
        ListNode cur = pre.next;
        ListNode next = cur.next;
        pre.next = swapPairs(next);
        cur.next = pre;
        return cur;

    }

 

 

 

5.将链表回文

变成数组和递归方法

1.变成数组

class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<Integer>();

// 将链表的值复制到数组中
ListNode currentNode = head;
while (currentNode != null) {
vals.add(currentNode.val);
currentNode = currentNode.next;
}

// 使用双指针判断是否回文
int front = 0;
int back = vals.size() - 1;
while (front < back) {
if (!vals.get(front).equals(vals.get(back))) {
return false;
}
front++;
back--;
}
return true;
}
}

2.递归

 

 

class Solution {
private ListNode frontPointer;

private boolean recursivelyCheck(ListNode currentNode) {
if (currentNode != null) {
if (!recursivelyCheck(currentNode.next)) {
return false;
}
if (currentNode.val != frontPointer.val) {
return false;
}
frontPointer = frontPointer.next;
}
return true;
}

public boolean isPalindrome(ListNode head) {
frontPointer = head;
return recursivelyCheck(head);
}
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM