算法训练营DAY5 第二章 链表part02 补

首先补充链表part01 的双链表、递归法反转链表

双链表

单链表中的指针域只能指向节点的下一个节点。

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

双链表 既可以向前查询也可以向后查询

关键点:注意哨兵指针的初始化,前后都指向自己;在查询函数中,使用中点下标简化查询中的cur指针移动次数,从哨兵指针开始向后移动cur指针时,需要注意for循环中“i”,是将指针移动到目标位置,从哨兵指针开始向前移动cur指针时,for循环中“i”是将指针移动到目标位置;在头插入与尾插入函数中,注意四步插入法,此时还不需要引入临时指针。在插入函数中,从前往后查询需要将cur指针移动到

class MyLinkedList {
public:
       // 定义双向链表节点结构体
    struct DList {
        int elem; // 节点存储的元素
        DList *next; // 指向下一个节点的指针
        DList *prev; // 指向上一个节点的指针
        // 构造函数,创建一个值为elem的新节点
        DList(int elem) : elem(elem), next(nullptr), prev(nullptr) {};
    };

    // 构造函数,初始化链表
    MyLinkedList() {
        sentinelNode = new DList(0); // 创建哨兵节点,不存储有效数据
        sentinelNode->next = sentinelNode; // 哨兵节点的下一个节点指向自身,形成循环
        sentinelNode->prev = sentinelNode; // 哨兵节点的上一个节点指向自身,形成循环
        size = 0; // 初始化链表大小为0
    }
    
    int get(int index) {
        if (index<0||index>=size){return -1;}
        int mid=size>>1;
        int num;
        DList* curNode=sentinelNode;
        if(indexnext;
            }
        }else {
            for(int i=0;iprev;
            }
        }
        num=curNode->elem;
        return num;
    }
    
    void addAtHead(int val) {
        DList* newNode=new DList(val);
        DList* nextNode=sentinelNode->next;
       //双链表四步法插入新节点
        newNode->next=nextNode;
        nextNode->prev=newNode;
        sentinelNode->next=newNode;
        newNode->prev=sentinelNode;
        size++;
    }
    
    void addAtTail(int val) {
        DList* newNode=new DList(val);
        DList* beforeNode=sentinelNode->prev;
        newNode->next=sentinelNode;
        sentinelNode->prev=newNode;
        beforeNode->next=newNode;
        newNode->prev=beforeNode;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index>size){return;}
        if(index<=0){addAtHead(val);return;}
        int mid=size>>1;
        int num;
        DList* curNode=sentinelNode;
        if(indexnext;
            }
            DList* newNode=new DList(val);
            DList* tmp=curNode->next;
            newNode->next=tmp;
            tmp->prev=newNode;
            curNode->next=newNode;
            newNode->prev=curNode;
        }else {
            for(int i=0;iprev;
            }
            DList* newNode=new DList(val);
            DList* tmp=curNode->prev;
            curNode->prev=newNode;
            newNode->next=curNode;
            newNode->prev=tmp;
            tmp->next=newNode;
        }
        size++;        
    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>(size-1)){
            return;
        }
        int mid=size>>1;
        int num;
        DList* curNode=sentinelNode;
        if(indexnext;
            }
            DList*next=curNode->next->next;
            DList*toDelete=curNode->next;
            curNode->next=next;
            next->prev=curNode;
            delete toDelete;
        }else {
            for(int i=0;iprev;
            }
            DList* prev=curNode->prev->prev;
            DList*toDelete=curNode->prev;
            curNode->prev=prev;
            prev->next=curNode;
            delete toDelete;
        }
        size--;       
    }
private:
    int size; // 链表的大小
    DList *sentinelNode; // 哨兵节点的指针
};

/**
 * 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);
 */

206.反转链表

206. 反转链表 - 力扣(LeetCode)

使用递归法做反转,首先可以用类似双指针的写法,定义一个反转函数,函数中有两个参数,reverse(pre,cur);当当前指针cur指向NULL时,pre指针将会是反转后的链表的头节点;函数中引入临时指针储存当前指针的下一个指针,然后更新cur->next=pre;函数返回reverse(cur,tmp)从而实现新一轮反转,直到cur==NULL.

/**
 * 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* reverse(ListNode* pre,ListNode* cur){
        if(cur==NULL){return pre;}
        ListNode*tmp=cur->next;
        cur->next=pre;
        return reverse(cur,tmp);
    }
    ListNode* reverseList(ListNode* head) {
        return reverse(NULL,head);
    }
};

递归法思路二:这个办法比较晦涩,嵌套使用反转函数,

/**
 * 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* reverseList(ListNode* head) {
        //
        if(head==NULL){return NULL;}
        if(head->next==NULL){return head;}

        ListNode*last=reverseList(head->next);
        head->next->next=head;
        head->next=NULL;
        return last;
    }
};

让我们用一个长度为4的链表为例来解释这个递归的 reverseList 函数。假设初始链表为:

1 -> 2 -> 3 -> 4 -> NULL

递归过程图解

初始调用
reverseList(1)
递归展开
  1. 第一层递归 (head = 1)

    • head->next 是 2,不为空,继续递归

    • 调用 reverseList(2)

  2. 第二层递归 (head = 2)

    • head->next 是 3,不为空,继续递归

    • 调用 reverseList(3)

  3. 第三层递归 (head = 3)

    • head->next 是 4,不为空,继续递归

    • 调用 reverseList(4)

  4. 第四层递归 (head = 4)

    • head->next 是 NULL,触发终止条件

    • 返回 head (即 4)

递归回溯

现在开始逐层回溯并反转链表。

  1. 第三层递归 (head = 3)

    • last 是 4(来自上一层的返回)

    • 执行 head->next->next = head3->4->next = 3,即 4->next = 3

    • 执行 head->next = NULL3->next = NULL

    • 链表状态:

      4 -> 3 -> NULL
      1 -> 2 -> 3 -> NULL (但 3 的 next 已经是 NULL)
    • 返回 last (即 4)

  2. 第二层递归 (head = 2)

    • last 是 4(来自上一层的返回)

    • 执行 head->next->next = head2->3->next = 2,即 3->next = 2

    • 执行 head->next = NULL2->next = NULL

    • 链表状态:

      4 -> 3 -> 2 -> NULL
      1 -> 2 -> NULL (但 2 的 next 已经是 NULL)
    • 返回 last (即 4)

  3. 第一层递归 (head = 1)

    • last 是 4(来自上一层的返回)

    • 执行 head->next->next = head1->2->next = 1,即 2->next = 1

    • 执行 head->next = NULL1->next = NULL

    • 链表状态:

      4 -> 3 -> 2 -> 1 -> NULL
    • 返回 last (即 4)

最终结果

链表被反转为:

4 -> 3 -> 2 -> 1 -> NULL

你可能感兴趣的:(算法训练营DAY5 第二章 链表part02 补)