代码随想录算法训练营第三天|203.移除链表元素 、707.设计链表、206.反转链表、

今日任务

  • 链表理论基础
  • 203.移除链表元素
  • 707.设计链表
  • 206.反转链表

链表理论基础

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的入口节点称为链表的头结点也就是head。
如图所示: 链表1
在这里插入图片描述

链表的类型

单链表

双链表

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

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

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

如图所示:
在这里插入图片描述

循环链表

链表首尾相连。

循环链表可以用来解决约瑟夫环问题。

在这里插入图片描述

链表的定义

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

也可以不定义构造函数,C++会默认生成一个构造函数,但他不会初始化任何成员变量。

  1. 通过自己定义构造函数初始化节点:
ListNode * head = new ListNode(5);
  1. 使用默认构造函数初始化节点
ListNode* head = new ListNode();
head->val = 5;

链表的操作

删除节点

在这里插入图片描述
在c++中需要手动释放D节点,释放此块内存。

添加节点

在这里插入图片描述

性能分析(对比数组)

在这里插入图片描述

203.移除链表元素 (简单)

题目链接:
203.移除链表元素

**题目描述:**删除链表中等于给定值 val 的所有节点。
方法一:头节点和非头节点分别处理

/**
 * 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){//若头节点不为空且值为val
            ListNode* q = head; //用q记录要删除的节点
            head = head->next;  //由于是头节点,故head后移便是在逻辑上删除了该节点
            delete q;           //释放内存
        }
        //删除非头节点
        ListNode* p = head;  //用p作为遍历指针
        while(p != NULL && p->next != NULL){//若p和p的下一个节点不为空
            if(p->next->val == val){  //若p的下一个节点值等于val
                ListNode* q = p->next;//用q记录要删除的节点
                p->next = p->next->next;//逻辑上删除
                delete q;// 释放内存
            }else{
                p = p->next;//否则接着遍历
            }
        }   
        return head;
    }
};

注:C++中释放节点内存 直接delete 即可。
方法二:设一个虚拟头节点来统一头节点和非头节点的操作。

/**
 * 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* Lhead = new ListNode(0);
         Lhead->next = head;
         ListNode* p = Lhead;
         while(p->next != NULL){
             if(p->next->val == val){
                 ListNode* q = p->next;
                 p->next = p->next->next;
                 delete q;
             }
             else{
                 p = p->next;
             }
         }
         head = Lhead->next;//虚拟头节点需要做进一步的处理,head即Lhead->next,再释放虚拟头节点Lhead
         delete Lhead;
         return head;
    }
};

注1:这里涉及C++结构体(struct)的使用,定义与声明和C区别不大,主要的区别在于C++中可以使用new动态创建结构体变量,此时必须是结构体指针类型。访问时,普通结构体变量使用成员变量访问符为".“,指针类型的结构体变量使用的成员便令访问符为”->"。

#include <iostream>

using namespace std;

struct Student
{
    int Code;
    char Name[20];
    char Sex;
    int Age;
}Stu,StuArray[10],*pStu;

int main(){

    Student *s = new Student();  // 或者Student *s = new Student;
    s->Code = 1;//指针类型访问成员变量使用->
    cout<<s->Code;

    delete s;
    return 0;
}

注2:结构体初始化方法有以下三种:
1.利用结构体自带的默认构造函数
2.利用带参数的构造函数
3.利用默认无参的构造函数

struct ListNode {
      int val;
      ListNode *next;
      ListNode() : val(0), next(nullptr) {}//其实就是无参构造函数
      ListNode(int x) : val(x), next(nullptr) {}//只传val的单参构造函数
      ListNode(int x, ListNode *next) : val(x), next(next) {}//有参构造函数
  };

707.设计链表(中等)

题目链接:707.设计链表

class MyLinkedList {
public:
    struct LinkedNode{
        int val;
        LinkedNode* next;
        LinkedNode() : val(0), next(nullptr){}
        LinkedNode(int val) : val(val), next(nullptr){}
    };
    //初始化链表
    MyLinkedList(){
        _dummyhead = new LinkedNode(0);//一个虚拟头节点,并非真正的头节点
        _size = 0;//链表大小
    }
    //取第index个节点
    int get(int index) {
        if(index >= _size || index < 0) {
            return -1;
        }
        LinkedNode* LNode = _dummyhead->next;//指向第一个非空节点,
        while(index--){//
            LNode = LNode->next;
            
        }       
        return LNode->val; 
    }
    //头插法插入新的节点
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);//创建一个值为val新节点
        newNode->next = _dummyhead->next;
        _dummyhead->next = newNode;
        _size++;
        }
        
    //尾插法插入新的节点
    void addAtTail(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* p = _dummyhead;
        while(p->next != nullptr){//切记是判断p->next是否为空指针
            p = p->next;
        }
        p->next = newNode;
        _size++;
    }
    //在某个节点前插入新节点
    void addAtIndex(int index, int val) {
        if(index > _size) return;//若index大于链表长度,直接返回
        if(index < 0)index = 0;//index小于0,则直接头插   
        LinkedNode* preNode = _dummyhead;
        LinkedNode* newNode = new LinkedNode(val);
        while( index--){
            preNode = preNode->next;
        }
        newNode->next = preNode->next;
        preNode->next = newNode;
        _size++;
    }
    //删除第index个节点
    void deleteAtIndex(int index) {
        if(index >= _size || index < 0){
            return;
        }else{
        LinkedNode* pNode = _dummyhead;
        while(index--) {//至此找到index-1处的节点
            pNode = pNode->next;
        }
        LinkedNode* qNode = pNode->next;//qNode指向要删除节点
        pNode->next = pNode->next->next;
        delete qNode;
        _size--;
        }
    }
    //打印链表
    void printLinkedList(){
        LinkedNode* p = _dummyhead;
        while (p->next != nullptr){
        cout << p->next->val << " ";
        p = p->next;
            } 
            cout << endl;
        }
  //定义成员变量
  private:
  int _size;
  LinkedNode* _dummyhead;
};

注:在编写链表的初始化方法时,声明一个虚拟头节点和链表的长度初值作为链表的成员变量,变量应该是private类型的,置于末尾是为了不截断public中的方法。

206.反转链表

题目链接:206.反转链表

题目建议:用迭代或者递归做?
方法一:双指针法

/**
 * 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* pre = NULL;//前指针,需要赋初值为NULL,否则会报错
        ListNode* p;//用于存放cur->next的指针
        ListNode* cur = head;
        while(cur) {
            p = cur->next;
            cur->next = pre;
            pre = cur;
            cur = p;
        }//至此p和cur指针均指向NULL,pre指向反转后的链头节点
        return 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;//当前指针为NULL,直接返回pre
        ListNode* temp = cur->next;//temp记录cur->next指针
        cur->next = pre;//反转指针方向
        return reverse(cur,temp);//依次向后循环
    }
    ListNode* reverseList(ListNode* head) {
        return reverse(NULL, head);//递归的开头NULL和头节点
    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值