时间复杂度:算法的时间开销与问题规模之间的关系
单链表
特点:与数组与vector对象不同,其属于非顺序存储,使用next访问下一个元素,数据存储时不仅要存储数据,还需要存储下一个节点的地址,最后一个节点指向空值
1.插入元素
①带头节点,插入元素不需要单独定义表头,但是在传递参数时需要包含头节点
②不带头节点,插入元素时需要单独定义表头,传递参数时不需要包含头节点
2.删除元素
利用next来进行,通常需要获知第一个节点
3.创建链表
//单链表节点定义c++
struct ListNode{
int val;
ListNode *next;
//下面对于代码中进行初始化链表很重要
ListNode() : val(0), next(nullptr) {} //初始化当前结点值为默认值0,指针为空
ListNode(int x) : val(x), next(nullptr) {} //初始化当前结点值为x,指针为空
ListNode(int x, ListNode *next) : val(x), next(next) {} //初始化当前结点值为x,下一个绩点为next
};
//分配新节点 上面的ListNode是可以更改的,属于自定义变量名
ListNode *head = nullptr;//定义一个初始为空的链表
head = new ListNode;//分配新节点
head->value = 12.5;
head->next = nullptr;//表示链表最后一个节点
删除链表中的某些特定元素
#include<iostream>
#include<vector>
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {} //初始化当前结点值为默认值0,指针为空
ListNode(int x) : val(x), next(nullptr) {} //初始化当前结点值为x,指针为空
ListNode(int x, ListNode *next) : val(x), next(next) {} //初始化当前结点值为x,下一个绩点为next
};
//class需要在main的前面
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if (head == nullptr) return NULL;
ListNode *H = new ListNode;
H->next = head;
ListNode *p = H;// new head
//在这里修改了p,H也会随之变化
while (p->next != nullptr) {
//H->next = p;
if (p->next->val == val) {
p->next = p->next->next;
}
else
{
p = p->next;
}
}
return H->next->next;
}
};
int main() {
Solution solution;
int nums[] = { 1, 2, 6, 3, 4, 5, 6 };
int length = sizeof(nums) / sizeof(int);
ListNode *head=new ListNode;
ListNode *H = new ListNode;
head = H;
//head = (ListNode)malloc(sizeof(ListNode));
//给链表中赋值
for (int i = 0;i < length; i++) {
ListNode *p = new ListNode;//表头,默认值为0
p->val = nums[i];
//p->next = nullptr;
H->next = p;
H = p;
}
int val = 6;
head = solution.removeElements(head, val);
return 0;
}
上面的head是没办法通过p的改变来进行修改,只能够重新构建一个链表。这个链表名为p,通过修改p,H的值的索引会发生改变(这里我不太懂)
if (head == nullptr) return NULL;
ListNode* p1 = head;
ListNode* p2 = new ListNode(head->val);
//p2 = head->val
while (p1->next != nullptr) {
ListNode* tmp = new ListNode(p1->next->val);
tmp->next = p2;
p2 = tmp;
p1 = p1->next;
}
return p2;
if (head == nullptr || head->next == nullptr) return head;
ListNode* p = head;
ListNode* pre = nullptr;
while (p) {
ListNode* pnext = p->next;
p->next = pre;
pre = p;
p = pnext;
}
return pre;
以上两个代码都是解决链表反转问题,为什么两个head的变化不一样,前者不会跟随p1变化,后者会跟随p变化
双链表
特点:每个数据结点都有两个指针,分别指向直接前驱与后继
//双链表节点定义c++
struct ListNode{
int val;
ListNode *next;
ListNode *last;
//ListNode(int x):val(x),next(nullpter){}
};
循环链表:单链与双链表
链表的缺点:必须从第一个节点开始遍历所有数据