单向链表及相关算法

链表

顺序表缺点

插入删除时需要移动大量元素,为此设计链表

链表定义 :

链表由结点组成,每个节点链接串联,分为单向链表,双向链表,循环链表等。

A→B→C→D→NULL

B是C的前驱节点,是A的后继节点

链表优缺点

优点:
1.链式存储,随时增加元素随时分配
2.插入删除时间复杂度O(1)
缺点:
索引查找时间复杂度O(n)

指针概念:
int n = 6;
int *p = # 

p = 0x123456
*p = 6

单向链表增删改查:
1.结点创建
/*创建结点结构体*/
struct ListNode{
	int val; //数据域,这里定义类型为int
	struct ListNode *next; //指针域,指向后继结点地址
	//两种构造方式
	ListNode(){} //空
	ListNode(int x):
		val(x),next(NULL){}
};
2.链表创建

头文件

#include<iostream>
#include<cstdlib>
using namespace std;
//随机链表还有ctime库

尾插法:从链表尾部插入,创建后继节点,后继节点地址赋给前继节点next,指针移到后继节点,注意链表尾节点应为null。

> p:移动指针
> curr:新创结点
> head:头结点

#include<iostream>
using namespace std;
/*创建结点结构体*/
struct ListNode{
	int val;
	struct ListNode *next;
	//两种构造方式
	ListNode(){} //空
	ListNode(int x):
		val(x),next(NULL){}
};
ListNode* CreateListByTail(int a,int b){
	ListNode *head = new ListNode(a),*curr,*p; //new+数据类型返回数据地址
	p = head;
	curr = new ListNode(b);
	p->next = curr;
	p = curr;
	//p->next = Null;
	return head;
}	

头插法:每次从头结点前进行插入,注意数组插入应为逆序

> p:移动指针
> head:头结点

#include<iostream>
using namespace std;
/*创建结点结构体*/
struct ListNode{
	int val;
	struct ListNode *next;
	//两种构造方式
	ListNode(){} //空
	ListNode(int x):
		val(x),next(NULL){}
};
ListNode* CreateListByHead(int a,int b){
	ListNode *head = new ListNode(b),*p;
	p = new ListNode(a);
	p->next = head;
	head = p;
	return head;
}
3.链表打印

循环输出所有节点至NULL。

void ListDisplay(ListNode *Head){
	ListNode *p = Head
	while(p!=NULL){
		cout<<p->val<<"->";
		p = p->next;
	}
	cout<<"NULL"<<endl;
}
4.链表索引

对于一个头结点List1,求索引i结点。
时间复杂度O(n),空间复杂度O(1)。

ListNode *GetListNode(int i,ListNode *List1){
	ListNode *p = List1;
	int j = 0;
	while(p&&j<i){
		p = p->next;
		++j;
	}
	if(!temp||j>i) //j>i防止i<0
	{return NULL;}
	return p;
}
4.链表查找

对于一个头结点Head,给定一个值v,找链表数据域上第一个等于v的结点。
时间复杂度O(n),空间复杂度O(1)。

ListNode *FindNodeByValue(int v,ListNode *Head){
	ListNode *p = Head;
	while(p){
		if(p->val == v) return p;
		p = p->next;
	}
	return NULL;
}
4.链表结点插入

对于一个头结点Head,在索引i后插入值为v的结点
时间复杂度O(n),空间复杂度O(1)。

void ListInsertNode(int i, int v, ListNode *Head) {
	ListNode *p = Head, *vtx = new ListNode(v);
	int j = 0;
	while (p && j < i) {
		p = p->next;
		++j;
	}
	if (p || j <= i) {
		vtx->next = p->next;
		p->next = vtx;
	}
}
4.链表结点删除

对于一个头结点Head,删除索引i处的结点
注意使用函数释放内存
时间复杂度O(n),空间复杂度O(1)。

void ListDeleteNode(int i, ListNode *Head) {
	ListNode *p1 = Head, *p2;
	int j = 0;
	while (p1 && j < i) {
		p2 = p1;
		p1 = p1->next;
		++j;
	}
	if (p1 || j <= i) {
		if (p1 == Head) {
			Head = p1->next;
			delete p1;
		} else {
			p2->next = p1->next;
			delete p1;
		}
	}
}
5.链表销毁

链表所有结点内存释放,头结点置空。
注:头结点需要使用二级指针 ,因为链表头要置空,普通指针传参无法使外部指针变量变化,外部会得到一个内存已经释放的野指针。
时间复杂度O(n),空间复杂度O(1)。

void ListClear(ListNode **Head){
	ListNode *pHead = *Head;
	while(pHead){
		ListNode *p = pHead->next;
		delete pHead;
		*pHead = p;
		}
	*Head = NULL;
}
5.链表翻转
ListNode* reverse(ListNode *head){
	ListNode *p1 = NULL; //一个前值
	ListNode *p2 = head;
	ListNode *p3 = p2;//一个后值
	while(p2){
		p3 = p2->next;
		p2->next = p1;
		p1 = p2;
		p2 = p3;
	}
	return p1;
}
算法:
1.双指针

1.链表合并——双指针各指一链表再合并
2.获得倒数第k个结点/中点——快慢指针
4.环/相交——快慢指针,找关系确定快慢指针步数

2.哈希表

需要记录链表中val值时

3.虚拟头结点

头结点有可能发生改变时,可以创建虚头结点(hair)

ListNode *hair = new ListNode(0,head);
head = hair->next //虚拟头节点的下一个地址指向头结点。

3.双向链表(待整理)
注意事项:

1.赋值后先移位
2.判断清楚交换顺序(画图)
参考:
1.画解链表
2.C++:数据结构-链表创建
3.C++ 单链表创建、插入和删除

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值