目录
链表是一种通过指针串在一起的线性结构,每个节点都由数据域和指针域组成,数据域存放节点数据,指针域存放指向下一个节点的指针,最后一个节点的指针指向null,也即这个指针为空指针。

随想录中,标准的单链表定义如下:
- struct ListNode {
- int val; // 数据域里的数据
- ListNode *next; // 指针域里指向下个节点的指针
- ListNode(int x) : val(x), next(NULL) {} // 构造函数,直接定义并初始化一个节点的数据域值为x
- };
-
- ListNode* head = new ListNode(5); // 通过自己定义的构造函数来初始化节点,直接赋值为5
-
- ListNode* head = new ListNode();
- head->val = 5; // 使用默认的构造函数来初始化节点,但是这里需要自己赋值

力扣里已经定义好了链表,所以我们只需要使用ListNode* 来定义指针即可。
这道题需要我们删除和目标值相同的节点,所以我们的思路简单粗暴,一个一个比下去然后遇到就删除就是了,但是这里有一个问题,就是我们要如何删除一个节点呢,其实很简单,要删除一个节点分三步,第一步,定义一个指针指向该节点,第二步,将原本指向该节点的指针指向这个节点的下一个节点,第三步,删除我们新定义的这个指针(同时把指针指向的节点也删了)。
我们的代码有两种做法,一种是直接开干,将要删的节点分为头结点和中间节点,头结点先删,中间节点再用一个while语句来删;另一种是设置一个虚拟头结点,这样就不存在需要删除头结点的情况了,只需要删除中间节点即可,但是最后要注意,返回的是我们定义的虚拟头结点的下一个节点指针。
- /**
- * 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;
- 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;
- }
- }
- return head;
- }
- };
- class Solution {
- public:
- ListNode* removeElements(ListNode* head, int val) {
- ListNode* dummy = new ListNode(0);
- dummy->next = head;
- ListNode* cur = dummy;
- while (cur->next != NULL) {
- if(cur->next->val == val) {
- ListNode* tmp = cur->next;
- cur->next = cur->next->next;
- delete tmp;
- }
- else {
- cur = cur->next;
- }
- }
- head = dummy->next;
- return head;
- }
- };
设计一个链表,实现以下功能:
这就是最简单的手搓链表了(bushi),在这里我们可以像上面那样给链表的前面添加一个虚拟头结点dummy,这样就不用考虑对头结点的特殊情况了,所有节点都一视同仁。
这就没什么思路不思路的了,思想很简单,实现是关键,真正写出来并且一点错没有,那就可以了,具体实现就看下面的代码吧。
注意,当你要开始复习链表的时候,就照着这个代码多抄多背,以后面试再也不用担心!
- class MyLinkedList {
- public:
- // 定义链表节点结构体
- struct LinkedNode {
- int val;
- LinkedNode* next;
- LinkedNode(int val) : val(val), next(nullptr){}
- };
-
- // 初始化链表
- MyLinkedList() {
- dummy = new LinkedNode(0); // 定义一个虚拟头结点
- size = 0; // 链表的初始长度为0
- }
-
- // 获取第index个节点数值,如果index非法则直接返回-1,index从0开始
- int get(int index) {
- if (index < 0 || index > size - 1) {
- return -1;
- }
- LinkedNode* cur = dummy->next;
- while (index--) { // index可以看作数组下标,cur是从下标为0的节点开始的,所以这里循环index次没错
- cur = cur->next;
- }
- return cur->val;
- }
-
- // 在链表前面插入一个节点,插入完后新的节点称为链表的新头结点
- void addAtHead(int val) {
- LinkedNode* newNode = new LinkedNode(val);
- newNode->next = dummy->next;
- dummy->next = newNode;
- size++;
- }
-
- // 在链表最后添加一个节点
- void addAtTail(int val) {
- LinkedNode* newNode = new LinkedNode(val);
- LinkedNode* cur = dummy;
- while (cur->next != nullptr) {
- cur = cur->next;
- }
- cur->next = newNode;
- size++;
- }
-
- // 在链表中第index个节点前插入新节点
- void addAtIndex(int index, int val) {
- if (index > size) return;
- if (index < 0) index = 0;
- LinkedNode* newNode = new LinkedNode(val);
- LinkedNode* cur = dummy;
- while (index--) {
- cur = cur->next;
- }
- newNode->next = cur->next;
- cur->next = newNode;
- size++;
- }
-
- // 删除第index个节点
- void deleteAtIndex(int index) {
- if (index > size - 1 || index < 0) {
- return;
- }
- LinkedNode* cur = dummy;
- while (index--) {
- cur = cur->next;
- }
- LinkedNode* tmp = cur->next;
- cur->next = cur->next->next;
- delete tmp;
- tmp = nullptr;
- size--;
- }
-
- // 打印链表
- void print() {
- LinkedNode* cur = dummy;
- while (cur->next != nullptr) {
- cout << cur->next->val << " ";
- cur = cur->next;
- }
- cout << endl;
- }
- private:
- int size;
- LinkedNode* dummy;
- };

反转链表,就是从一个链表的第一个有效节点开始,逐一移出原链表,放到新链表的开头,
这样虽然原链表是从前往后拿走节点,但是新链表是从后往前一个一个加进去,这就完成了反转。
- class Solution {
- public:
- ListNode* reverseList(ListNode* head) {
- ListNode* temp; // 保存cur的下一个节点
- ListNode* cur = head; // 指向头结点
- ListNode* pre = NULL; // 新链表的头指针
- while (cur) { // 当原链表中还存在节点时
- temp = cur->next; // 保存cur的下一个节点,因为接下来要改变cur->next
- cur->next = pre; // 此时cur这个节点的下一个是新链表的第一个节点
- pre = cur; // 然后pre指针指向现在cur的这个结点
- cur = temp; // cur指向原链表的下一个结点
- }
- return pre;
- }
- };
- class Solution {
- public:
- ListNode* reverse(ListNode* pre,ListNode* cur){
- if(cur == NULL) return pre;
- ListNode* temp = cur->next;
- 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);
- }
-
- };