算法与数据结构之美——链表(二)

算法与数据结构之美——链表(二)

  • 理解指针或引用的含义
  • 警惕指针丢失和内存泄漏
  • 利用哨兵简化实现难度
  • 留意边界条件处理
  • 举例画图,辅助思考
  • 多写多练没有捷径
  • 链表练习题:

写出没有bug的链表,除了要多花时间之外,还需要掌握以下技巧

理解指针或引用的含义

不管是指针还是引用,都是存储着所指对象的地址

警惕指针丢失和内存泄漏

对于初学者,以单链表为例,代码如下:
算法与数据结构之美——链表(二)_第1张图片

p->next = x; //p的next指针指向x结点
x->next = p->next;//将x结点的next指针指向b结点

在第一行代码结束之后,已经不在指向原先的结点b,而是指向结点x;
像java有虚拟机自动管理的不需要考虑那么多,其他的语言在删除链表结点的时候,切记要手动释放内存空间,防止出现内存泄漏;

利用哨兵简化实现难度

对于单链表中间结点的插入和删除,只需要两行代码即可搞定。但是首结点和尾结点的插入和删除逻辑就不一样了。
引入哨兵结点是为了解决边界问题,例如:head表示头结点指针,指向链表的第一个结点。在任何时候,不管链表是否为空,head指针都会指向这个哨兵结点,这种被称为带头链表。
算法与数据结构之美——链表(二)_第2张图片

留意边界条件处理

想写出没有bug的链表代码,边界条件需要考虑全面,以下是两个常用的检查链表是否正确的边界条件:

  • 如果链表为空,代码能否正常工作?
  • 如果链表只包含一个结点时,代码能否正常工作?

举例画图,辅助思考

多写多练没有捷径

链表练习题:

1、反转一个单链表:
https://leetcode-cn.com/problems/reverse-linked-list/
设定两个指针,依次反转;注意边界条件,以及循环终止的条件即可。

class ListNode{
    int val;
    ListNode next;
    ListNode(int x){
        val = x;
    }
}
class Solution{
    public ListNode reverseList(ListNode head){
        if(head == null || head.next == null){
            return head;
        }
        ListNode p1 = head;
        ListNode p2 = head.next;
        head = null;
        while(p2!=null){
            ListNode temp = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = temp; 
        }
        return p1;
    }
}

2、链表中环的检测
https://leetcode-cn.com/problems/linked-list-cycle/
这题的关键是设定两个快慢指针,当快慢指针相遇,说明链表有环,反之无环;

class ListNode{
    int val;
    ListNode next;
    ListNode(int x){
        val = x;
    }
}
public class Solution{
    public boolean hasCycle(ListNode head){
        if(head == null || head.next == null){
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while(fast!=null&&fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow==fast){
                return true;
            }
        }
        return false;
    }
}

3、合并两两有序链表

public class Solution{
    public ListNode combine(ListNode head1,ListNode head2){
        if(head1==null)
            return head2;
        else if(head2==null)
            return head1;
        ListNode p1 = head1;
        ListNode p2 = head2;
        ListNode head = new ListNode(0);
       ListNode p3 = head;
       while(p1!=null&&p2!=null){
           if(p1.val<=p2.val)
           {
               p3.next=p1;
               p1 = p1.next;
           }else{
               p3.next=p2;
               p2 = p2.next;
           }
           p3 = p3.next;
       }
       if(p1!=null) {p3.next = p1; }
       if(p2!=null) {p3.next = p2; }
       return head.next;       
    }
}

4、删除链表倒数第n个结点
设置三个指针prev,p1 p2,二者分别指向头结点,p2,先向后移n个结点,p1 p2同时向后移,直到p2到达尾结点,prev用于记录p1结点的前驱节点,方便最终对p1删除。

public ListNode deleteLastKth(ListNode head,int n){
    if(head == null) return head;
    ListNode p1 = head;
    ListNode p2 = head;
    ListNode prev = null
    for(int i=0;i<n;i++){
        p2 = p2.next;
    }
    while(p2.next!=null){
        prev = p1;
        p1 = p1.next;
        p2 = p2.next;
    }
    if(prev == null)
    {
        head = head.next;
    }else{
        prev.next = prev.next.next;
    }
    return head;    
}

5、求链表的中间结点
同样是设定快慢指针,快指针是慢指针速度的两倍,当快指针到达尾结点的时候,此时慢指针,即为链表的中间节点;

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

6、两两交换链表中的结点
https://leetcode-cn.com/problems/swap-nodes-in-pairs/

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head==null||head.next==null)
            return head;
        
        ListNode prev = new ListNode(0);
        prev.next = head;
        ListNode self = head.next;
        while(prev.next!=null&&prev.next.next!=null)
        {
            ListNode p1 = prev.next;
            ListNode p2 = p1.next;
            ListNode p3 = p2.next;
            
            prev.next = p2;
            p1.next =p3;
            p2.next =p1;
            prev = p1;
        }
        return self;
    }
}

你可能感兴趣的:(数据结构与算法之美)