day03-链表part1
目录
一、链表基础知识
1.概念
2.链表的分类
3.单链表定义
4.链表基本操作
二、移除链表元素
三、设计链表
四、反转链表
一、链表基础知识
1.概念
链表是一种依靠指针连接起来的一种在存储上非连续的数据结构,由结点相连。结点包含两部分,数据部分和指针部分。
2.链表的分类
- 单链表
- 双链表
- 循环链表
3.单链表定义
// 单链表
struct ListNode {int val; // 节点上存储的元素ListNode *next; // 指向下一个节点的指针ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
4.链表基本操作
- 查询、搜索
- 插入
- 删除
二、移除链表元素
leetcode 203
在进行链表移除元素操作时,分为两种情况:
- 直接用head作为头指针,此时head需要特殊处理,因为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) {//先处理头节点(因为头节点没有前节点)while(head != nullptr && head->val == val){ListNode *q = head;head = head->next;delete q; }//再处理其他节点ListNode *p = head; //通过节点p遍历单链表while(p != nullptr && p->next != nullptr){if(p->next->val == val){ListNode *q = p->next;p->next = q->next;delete q;q=nullptr;}else{p = p->next;}}return head;}
};
2.设置虚拟节点指向头结点,这样就可以统一处理,不用分类讨论。参考代码如下:
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作ListNode* cur = dummyHead;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 = dummyHead->next;delete dummyHead;return head;}
};
三、设计链表
leetcode 707
设计链表设计删除、插入指定值的结点,或者删除、插入指定索引的节点,其核心思想就是要找到待删除节点或插入节点的前驱,然后进行指针变换即可。(这里采用的仍然是虚拟头结点的方法)
有一个要注意的点是,用来遍历链表的指针q初始值是p(虚拟头结点),还是head。通常要指向要找的结点,q的初始值是head,而要指向待找结点的前驱,其初始值为p。
在初始化链表时,还设置了size用来记录链表长度,注意插入或删除时,size的变化。参考代码如下:
class MyLinkedList {
public:struct ListNode{int val;ListNode *next;ListNode(int x):val(x),next(nullptr){}};MyLinkedList() {//用来设置不存放数据的头节点和链表长度p = new ListNode(0);size = 0;}int get(int index) {if(index<0 || index>size-1) return -1;ListNode *q = p->next;while(index--){q = q->next;}return q->val;}void addAtHead(int val) {ListNode *q = new ListNode(val);//注意p是不存放数据的头节点q->next = p->next;p->next = q;size++;//注意长度}void addAtTail(int val) {ListNode *t = new ListNode(val);ListNode *q = p;while(q->next!=nullptr){q=q->next;}q->next = t;size++;}void addAtIndex(int index, int val) {if(index>size) return ;else if(index==size) addAtTail(val);else{ListNode *t = new ListNode(val);ListNode *q = p;while(index--){q=q->next;}t->next = q->next;q->next = t;size++;}}void deleteAtIndex(int index) {if(index <0 || index>size-1) return ;ListNode *q = p;while(index--){q = q->next;}ListNode *t = q->next;q->next = t->next;delete t;t = nullptr;size--;}private:ListNode* p;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);*/
四、反转链表
leetcode 206
这道题有多种方法
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 *cur = head;ListNode *pre = NULL;while(cur){LinkList *t = cur->next;cur ->next = pre;pre = cur;cur = t;}return pre; // 返回反转后的头结点}
};
2.递归法
递归法与双指针法类似
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);}};
3.头插法
从第二个元素开始,将每个结点插入到虚拟头结点后面,从而实现反转,参考代码如下,注意头指针或首结点为空的情况和指针变换:
/*** 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==nullptr || head->next==nullptr) return head;ListNode *p = new ListNode(0);p->next = head;ListNode *q = head->next;head->next = nullptr;while(q!=nullptr){ListNode *t = new ListNode(q->val);t->next = head;p->next = t;head = t;q=q->next;} return head;}
};
4.尾插法
从首结点开始,全部插入到链表最后,尾插法通常用于有一指针指向链表尾节点的情况,这里用尾插法其实会增大时间复杂度,因此只给大家提供一个思路