Day3|链表Leetcode203. 移除链表元素 Leetcode707. 设计链表 Leetcode206. 反转链表

学习链表之前,需要对链表的理论知识进行一个理解:

我大概总结了以下几点:

1)链表的组成部分:有数据域,有指针域(用来指向下一个节点),两个需要注意的点:最后一个节点的指针域指向的是NULL,头节点是用head来表示的

2)链表的种类;单链表,双链表

3)链表的储存方式:非连续分布,是通过指针域中的指针连接起来,并占用内存

4)链表的定义:

//来自代码随想录
struct ListNode {
    int val;  // 节点上存储的元素,放到数据域中
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

5)链表中删除节点,增加节点的原理:

删除节点:删除节点的上一个节点的指针域指向删除节点的下一个数据域

注意在c++需要将删除节点的内存手动清除,下面是代码

ListNode* tmp = head;
delete tmp;

增加节点:在增加的位置的前节点的指针指向该节点,将该节点的指针指向后节点的数据域

下面就可以做题了:

Leetcode 203 移除链表元素

题目链接203 移除链表元素

两种方法:

第一种方法:基于移除元素的原理

直接上代码:

/**
 * 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* removeElements(ListNode* head, int val) {
        while(head!=NULL&&head->val==val){//处理头节点时,需要拿出来单独处理,头节点不能为空
            ListNode* tmp = head;
            head = head->next;//头指针后移,指向下一个节点,表示头节点已经被删除
            delete tmp;
        }
        //处理非头节点
        ListNode* cur = head;//新建一个指针,从head开始
//原因:删除节点要记录上一个节点的指针域,从head->next开始的话,无法记录上一个节点
        while(cur!=NULL&&cur->next!=NULL){
            if(cur->next->val==val){
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;//核心
                delete tmp;
            }else{
                cur = cur->next;//没有找到val,继续下一个节点
            }
        }
        return 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* removeElements(ListNode* head, int val) {
        ListNode* dummyhead = new ListNode(0); // 设置一个虚拟头结点
        dummyhead->next = head; // 将虚拟头结点指向head
        ListNode* cur = dummyhead;//建立cur新指针
        //剩下的和普通法一样,这里就不过多赘述
        while (cur->next != NULL) {
            if(cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        //这个可以把dummyhead删除,返回到head;
        //也可以直接写 return dummyhead->next;
        head = dummyhead->next;
        delete dummyHead;
        return head;
    }
};

Leetcode 707 设计链表

题目链接707 设计链表

本题目涉及到链表的删除,查找,添加,这基本上包括了链表的所有的基础操作

直接上代码加注释:

class MyLinkedList {
public:

    struct LinkedNode{
        int val;//数据值
        LinkedNode*next;//指针
        LinkedNode(int val):val(val),next(nullptr){}//变量函数
    };
    MyLinkedList() {//初始对象
        dummyHead = new LinkedNode(0);//新建一个虚拟头节点
        size = 0;//链表长度初始化
    }
    
    int get(int index) {//获得下表为index的值
            if(index<0||index>(size-1)){//不在去链表区间范围内
                return -1;
            }
            LinkedNode*cur = dummyHead->next;//建立新指针指向dummyHead的下个节点,为了使寻找区间值合法
            while(index--){//这里同样是为了使寻找区间合法
                cur = cur->next;//右移
            }
            return cur->val;//最后输出index结点的值
    }
    
    void addAtHead(int val) {//在链表首位加入一个新节点
        LinkedNode*newnode = new LinkedNode(val);//新建立一个节点
        //因为这里确定了插入节点的位置,所以就不需要在建立一个新的指针了
        newnode->next = dummyHead->next;//注意添加节点时的顺序,反过来时dummyHead->next的值会被改变
        dummyHead->next = newnode;

        size++;//链表节点数加一
    }
    
    void addAtTail(int val) {//增加到末尾位置,由于没有向head头指针一样的尾指针的,所以要建立一个新指针
        LinkedNode*newnode = new LinkedNode(val);//新节点
        LinkedNode*cur = dummyHead;//新指针
        while(cur->next!=nullptr){//最后一个节点指针指向的是空
            cur = cur->next;//遍历到最后一个节点
        }
        newnode->next = nullptr;//最后一个节点为空
        cur->next = newnode;//此时cur指向的节点连接到新节点上

        size++;//节点数加一
    }
    
    void addAtIndex(int index, int val) {
        if(index<0){//下表数小于0,将该节点放到第一个节点
            index = 0;//称之为0节点
        }
        if(index>size){
            return ;//超出范围直接退出循环
        }
        //下面讨论范围内的
            LinkedNode*newnode = new LinkedNode(val);//新建一个新节点
            LinkedNode*cur = dummyHead;//我们不知道具体的位置,所以我们要建立一个新指针
            while(index--){//不停遍历,找到index节点
                cur = cur->next;
            }//此时循环条件中为0时,就已经停止了,此时cur指针指向的是index下标节点的上一个节点
            newnode->next = cur->next;//这里的cur->next指的是index节点
            cur->next = newnode;//注意连接顺序顺寻
            size++;//节点加一
    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>size-1){
            return ;//删除节点超出范围直接pass
        }
        LinkedNode*cur = dummyHead;//建立新指针
        while(index--){
            cur = cur->next;//找到删除项,且cur指向删除的前一项
        }
        LinkedNode* tmp = cur->next;//定义目标节点的内存
        cur->next = cur->next->next;//删除节点
        delete tmp;//删除内存

        tmp = nullptr;//delete命令指示释放了tmp指针原本所指的那部分内存,
        //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
        //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间

        size--;//节点减一
    }
    //打印链表,根据题目要求来
    void printLinkedList(){
        LinkedNode*cur = dummyHead;//定义新指针和虚拟头
        while(cur->next!=nullptr){
            cout<next->val<<" ";//打印链表所有节点的值
            cur = cur->next;//遍历
        }
        cout<get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

Leetcode 206 反转链表

题目链接206 反转链接

本题目还是用两种方法;

首先介绍第一种,双指针法:

先画一个图更方便大家理解:

Day3|链表Leetcode203. 移除链表元素 Leetcode707. 设计链表 Leetcode206. 反转链表_第1张图片

加上代码会更好的理解:

/**
 * 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) {
        ListNode*temp;
        ListNode*cur = head;
        ListNode*pre = NULL;
        while(cur != NULL){
            temp = cur->next;//将cur后一个节点记录
            cur->next = pre; //改变链表指向
            pre = cur;//不停遍历,两指针前移
            cur = temp;//temp只用来节点记录,不用来移动
           
        }
        return pre;//此时pre指向的就是头节点
    }
};

另一种方法是递归法,下面是代码加注释:

/**
 * 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*temp = cur->next;//记录cur的下个节点
        cur->next = pre;//该指针方向
        //递归的思想和双指针中的遍历是等同的
        //pre = cur;
        //cur = temp;
        return reverse (cur,temp);//递归,相当于指针不停右移
    }
    ListNode* reverseList(ListNode* head) {
    //这里相当于初始化
    //ListNode*cur = head;
    //ListNode*pre = NULL;
        return reverse(NULL,head);
       
    }
};

end

你可能感兴趣的:(笔记,leetcode,链表,算法)