链表

64 阅读6分钟

[toc]

01移除链表元素

Problem: 203. 移除链表元素

思路

看到这题的第一想法是java处理链表会比较麻烦,因为没有指针。但是后面发现可以用对象完全替代指针

解题方法

加入一个虚节点,实现化繁为简的操作,可以统一处理所有的节点。然后通过cur!=null为终止条件进行遍历,当cur的值=val时删除该节点,java里删除不需要考虑到内存释放的问题,只需要pre.next=cur.next即可。

复杂度

时间复杂度: 每个节点遍历一次,算法复杂度取决于节点数量O(n)O(n)

空间复杂度: O(1)用了一个辅助空间

Code

/**
 * 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 removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode pre = dummy;
        ListNode cur = head;//前置节点
        while (cur != null) {
        if (cur.val == val) {
            pre.next = cur.next;
        } else {
            pre = cur;
        }
        cur = cur.next;
        
    }
    return dummy.next;
    }
}

02设计链表

Problem: 707. 设计链表

[TOC]

思路

讲述看到这一题的思路

解题方法

描述你的解题方法

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)O(n)

空间复杂度:

添加空间复杂度, 示例: O(n)O(n)

Code

class MyLinkedList {
    
public:
class LinkedListNode{
    public:
    int val;
    LinkedListNode* next;
    LinkedListNode(int val):val(val),next(nullptr){}
};
    MyLinkedList() {

        _dummyhead = new LinkedListNode(0);//直接将虚节点指向新建的节点,同时size置0使得链表为空
        _size=0;
    }
    
    int get(int index) {
        // if(index>(_size-1) || index<0){
        //     return -1;
        // }
        if (index > (_size - 1) || index < 0) {
            return -1;
        }

        LinkedListNode* cur = _dummyhead;
        while(index--){
            cur = cur->next;//指向下一个节点,直到i=index,这时候cur正好停在index的前一个
        }
        return cur->next->val;

    }
    
    void addAtHead(int val) {
        LinkedListNode* newNode = new LinkedListNode(val);
        newNode->next=_dummyhead->next;
        _dummyhead->next = newNode;
        _size++;
    }
    
    void addAtTail(int val) {
        LinkedListNode* newNode = new LinkedListNode(val);
        LinkedListNode* cur = _dummyhead;
        while(cur->next != nullptr){
            cur=cur->next;
        }//cur移动到最后一个节点
        cur->next=newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index<0 || index > (_size)){
            return;//无返回值函数也可以使用返回语句,作用是结束函数
        }

        LinkedListNode* newNode = new LinkedListNode(val);
        LinkedListNode* cur = _dummyhead;
        while(index--){
            cur = cur->next;//指向下一个节点,直到index=0,这时候cur正好停在index的前一个
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
        return;
    }
    
    void deleteAtIndex(int index) {
                if(index<0 || index > (_size-1)){
            return;//无返回值函数也可以使用返回语句,作用是结束函数
        }


        LinkedListNode* cur = _dummyhead;
        while(index--){
            cur = cur->next;//指向下一个节点,直到index=0,这时候cur正好停在index的前一个
        }
        LinkedListNode* tmp = cur->next;//使用tmp保存需要释放的指针,因为待会儿cur的next就要被覆盖了
        cur->next = tmp->next;
        delete tmp ;
        _size--;
        return;

    }
    private:
    LinkedListNode* _dummyhead;
    int _size;

};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

总结:index也表示从0节点到index节点的距离,所以当index--等于0的时候,那么恰好移动了index步;index插入的时候,假如链表有3个元素,那么index是可以等于3的,等价于尾插法。

03交换链表中的相邻节点

Problem: 24. 两两交换链表中的节点

[TOC]

思路

想到的是用双指针法,最后优化发现可以去掉一个指针。另外读题刚开始也没读懂,以为只交换一对就行

解题方法

用pre获取前驱节点,每次处理两个节点,p1,p2代表后续的第一个和第二个节点,链表顺序要从后往前考虑,先把p1插进去,再插p2进去

##复杂度

时间复杂度:

会遍历所有的节点,每两个节点就会有3次指针变换op,复杂度为O(n)O(n)

空间复杂度:

空间复杂度, 示例: O(1)O(1)

Code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode(0, head); // 引入虚节点简化操作
        ListNode *p1, *p2, *pre;
        pre = dummy;
        while (pre->next != nullptr && pre->next->next != nullptr) {
            // 记录两个需要交换的指针
            p1 = pre->next;
            p2 = p1->next;
            // 交换
            p1->next = p2->next;
            p2->next = p1;
            pre->next = p2;
            // pre往后移动
            pre = p1;
        }
        return dummy->next;
    }
};

04删除倒数第n个节点

Problem: 19. 删除链表的倒数第 N 个结点

[TOC]

思路

双指针法,一个先跑一个后面跑

复杂度

时间复杂度:

n

空间复杂度:

1有一个虚节点

Code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *newHead = new ListNode(0,head);
        ListNode *end, *pre_n;

        end = newHead;
        while (n--) {
            end = end->next;
        } // 往前n步走
        pre_n = newHead;
        while (end->next != nullptr) {
            pre_n = pre_n->next;
            end = end->next;
        }
        ListNode *tmp =pre_n->next;
        pre_n->next = pre_n->next->next;
        delete tmp;
        return newHead->next;
    }
};

05链表相交

Problem: 面试题 02.07. 链表相交

[TOC]

解题方法

遇到查找问题,需要从要查找的东西的本质出发,而交点的本质就是有相同的指针地址,那么如何找到这个地址呢?就需要考虑到链表的长度,当两个链表一样长的时候,就可以同时到达相交节点。

复杂度

时间复杂度:

两个链表各遍历一次复杂度为nn,求相交复杂度小于n, O(n)O(n)

空间复杂度:

O(1)O(1)

Code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int La,Lb;
        ListNode *pa,*pb;
        pa = headA;
        La = 0;
        pb = headB;
        Lb = 0;
        while(pa){
            pa = pa->next;
            La++;
        }
        while(pb){
            pb = pb->next;
            Lb++;
        }
        int diff;
        pa =headA;
        pb = headB;
        //如果a长a就先走,b长b就先走,直到两个指针指到同一个位置去
        if(La>Lb){
            diff = La-Lb;
            while(diff--){
                pa = pa->next;
            }
        }
        else{
            diff = Lb-La;
            while(diff--){
                pb = pb->next;
            }
        }

        while(pa&&pa!=pb){
            pa = pa->next;
            pb = pb ->next;
        }
        return pa;
        
    }
};

06判断是否有环

Problem: 142. 环形链表 II

[TOC]

解题方法

image.png

假设环的长度为n,快指针速度为f,慢指针速度为s,那么有f-

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)O(n)

空间复杂度:

添加空间复杂度, 示例: O(n)O(n)

Code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
        
    }
};