数据结构笔记(更新中)
数据结构与算法
本笔记是根据数据结构与算法基础(青岛大学-王卓)https://www.bilibili.com/video/BV1nJ411V7bd 归纳整理
"*"代表相对重要
1.类C语言补充

data(即数组名)和*data存放的是数组首地址

(ElemType*)表示强制类型转换成指针


2.线性表
2.1 顺序表示

2.1.1 顺序表的基本操作
//函数结果状态代码#define TRUE
#DEFINE ture 1
#DEFINE false 0
#define oK 1
#define ERROR0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值是函数结果状态代码
typedef int status;
typedef char ElemType;
1. 线性表L的初始化
status initlist_sq(sqlist &L){
L.elem=new ElemType[MAXSIZE];
if(!L.elem) exit(OVERFLOW);
L.legth=0;
return ok;
}
2. 线性表L的销毁与清空
void destroylist(sqlist &L){
if(L.elem) delete L.elem; //释放存储地址空间(delete为c++语法)
}
void clearlist(sqlist &L){
L.length=0;
}
3. L的长度及是否为空
int GetLength(SqList L){
return (L.length);
}
int lsEmpty(SqList L){
if (L.length==0) return 1;
else return o;
4. 顺序表的取值
int GetElem(SqList L,int i,ElemType &e){
if (i<1||i>L.length)
return ERROR;
e=L.elem[i-1];
return OK;
}
**2.1.2 顺序表的值查找(顺序查找)
int LocateELem(SqList L ElemType e){
//在线性表L中查找值为e的数据元素,返回其序号(是第几
个元素)
for (i=0;i< L.length;i++)
if (L.elem[i]==e) return i+1;//查找成功,返回序号return 0;
//查找失败,返回0
}
平均查找长度:

平均时间复杂度:
O(n)
特点:
? 随机存取
**2.1.3 顺序表的插入
插入位置i:1<=i<=n+1

平均查找长度:

平均时间复杂度:
O(n)
**2.1.4 顺序表的删除
插入位置i:1<=i<=n

平均查找长度:

平均时间复杂度:
O(n)
2.1.5 顺序表的总结


2.2 链式表示
2.2.1 概念

1.无头结点表示空表:

2.有头结点表示空表:

3.头结点的好处:

4.特点:
? 顺序存取
2.2.2 单链表
存储结构(类型定义):
typedef struct Lnode{
ELEMTYPE data;
struct londe *next;
}Londe,*Linklist;

*Linklist表示指向这个结构块的指针
定义指针:
Linklist L / Lnode *L
1.基本操作(带头结点)
1.1 单链表的初始化

1.2 单链表是否为空

*1.2 单链表销毁包括头结点
一个辅助指针


*1.3 单链表清空
两个辅助指针


注:
while(L)等价于while(L!=null)
循环条件不能为while(q)
*1.4 单链表长度
一个辅助指针


**2.取值
一个辅助指针
1<=i<=n


while循环里跳出的情况:
p为null(i>j) j=i i<1 (即: j>i)
符合if条件的:
p为null(i>j) 或 i<1 (即: j>i)
注:此代码为伪代码
**3.查找
一个辅助指针
1<=i<=n
返回指针地址


同:
{
p=L->next;
while(p){
if(p->data=a)
return p;
p=p->next;
}
return p;
}
不成熟总结:
while( ){
for( )
}
等价于while( && )
返回位置序号

平均查找长度:

平均时间复杂度:
O(n)
**4.插入 (之前)
两个辅助指针
1<=i<=n+1


vs对比

平均时间复杂度:
O(n)
注:插入和删除操作的完整过程包括查找操作,查找操作是O(n),而单个插入和删除操作只是移动指针,只是O(1)
**5.删除
两个辅助指针
1<=i<=n


解析:
p=L;j=0;
因为删除首元结点时要用到头结点,所以将L赋给p,而不是将L->next赋给p
while(p->next&&j
①确保 q指向的地址不为null;如果是while(p&&j
next就无意义(会报错) while(p) p->next:p会指向第n+1个元素(即null)
while(p->next) p->next:p会指向第n个元素
②p指向第i个结点前驱的固定格式:(循环次数不重要,不虚记忆)
p=L;j=0; while条件是j
p=L->next;j=1; while条件是j
p指向第i个结点的固定格式:
p=L;j=0; while条件是j
p=L->next;j=1; while条件是j
③if(!(p->next)||j>i-1) return error: 表示i不在
1<=i<=n这个区间范围内,就return
结论:
p指向第i个结点前驱,while条件是j
p指向第i个结点,while条件是j
平均时间复杂度:
O(n)
注:插入和删除操作的完整过程包括查找操作,查找操作是O(n),而单个插入和删除操作只是移动指针,只是O(1)
**6.建立链表
一个辅助指针
6.1头插法



平均时间复杂度:
O(n)
6.2尾插法
两个辅助指针


平均时间复杂度:
O(n)
2.2.3 循环链表



1.循环链表合并


时间复杂度:
O(1)
2.双向链表



1.插入

4.不可以和1.或2.换

2.删除


不需要去找该删除结点的前驱结点
找第i个结点的时间复杂度:O(n)
下面只执行一次:O(n)
2.3 总结

双向循环链表是用空间换时间




结论:
p指向第i个结点前驱,while条件是j
p指向第i个结点,while条件是j
2.4 应用

2.1 顺序

2.2 链式
尾插法



空间复杂度:
O(1)


#include
#include
#define LENGTH sizeof(struct node)
//定义一个节点
int len;//全局变量n
typedef struct node
{
int Coffficient;
int Exponent;
struct node* next;
}*Pnode,Node;
//创建一个单链表(尾插法)
Pnode Create_List()
{
Node * p,* tail, *head;
int a,b;
head = (Node*)malloc(LENGTH);//原型应为malloc(1*LENGTH),1省略
tail = head;
tail->next = NULL;
printf("请输入结点个数:");
scanf_s("%d", &len);
printf("请输入元素,系数= , 指数=:\n");
for (int i = 0; i < len; i++)
{
scanf_s("%d,%d", &a,&b);
p = (Node*)malloc(LENGTH);
p->Coffficient=a;
p->Exponent = b;
tail->next = p;
p->next = NULL;
tail = p;
}
return head;//f返回头结点
}
//按指数排序
void Sort_Linklist1(Node* phead) {//冒泡排序
Node* p = phead->next;
Node* q;
int temp1,temp2, i=0;
for (; i < len-1; i++, p = p->next) {
for (q = p->next; q!= NULL; q = q->next) {
if (p->Exponent < q->Exponent) {
temp1 = p->Exponent;
p->Exponent = q->Exponent;
q->Exponent = temp1;
temp2 = p->Coffficient;
p->Coffficient = q->Coffficient;
q->Coffficient = temp2;
}
}
}
}
//比较函数
int compare(int Exponent1, int Exponent2) {
if (Exponent1 > Exponent2)
return 1;
if (Exponent1Coffficient = a;
p->Exponent = b;
p->next = NULL;
(*temp)->next = p;
*temp = p;
}
Pnode Add_List(Node* p1, Node* p2) {
Node* front,*rear;
p1 = p1->next;//从头结点的下一节点开始遍历
p2 = p2->next;
int sum;
rear = (Node*)malloc(LENGTH);
front = rear;
while (p1 && p2) {
switch (compare(p1->Exponent, p2->Exponent)) {
case 1:
Attach(p1->Coffficient, p1->Exponent, &rear);
p1 = p1->next;
break;
case -1:
Attach(p2->Coffficient, p2->Exponent, &rear);
p2= p2->next;
break;
case 0:
sum = p1->Coffficient + p2->Coffficient;
Attach(sum, p2->Exponent, &rear);
p1 = p1->next;
p2 = p2->next;
break;
}
}
for (; p1; p1 = p1->next)//p2为空循环结束后继续遍历p1
Attach(p1->Coffficient, p1->Exponent, &rear);
for(;p2;p2=p2->next)//p1为空循环结束后继续遍历p2
Attach(p2->Coffficient, p2->Exponent, &rear);
rear->next = NULL;
return front;
}
//遍历链表
void Print_List(Node* phead)
{
Node* p = phead->next;//跳过头结点进行遍历
while (p != NULL)//尾结点为空时终止(尾结点的指针域为空)
{
printf("%dX^%d\t", p->Coffficient,p->Exponent);
p = p->next;
}printf("\n");
}
int main()
{
Node* s1,*s2,*s3;
printf("请先创建链表\n");
printf("输入Polynomial1");
s1= Create_List();
printf("指数从大到小排序后:\n");
Sort_Linklist1(s1);
Print_List(s1);
printf("输入Polynomial2");
s2= Create_List();
Sort_Linklist1(s2);
printf("指数从大到小排序后:\n");
Print_List(s2);
s3=Add_List(s1, s2);
printf("多项式相加后:\n");
Print_List(s3);
return 0;
}
3. 栈和队列
3.1 定义及特点

栈:




队:

特点:
栈:后进先出
队列:先进先出
3.2 应用


3.3 栈的表示和实现
1. 数据类型定义


SElemType是指元素类型

2. 顺序栈



1. 初始化

2. 判断是否为空及长度


3. 清空及释放


delete s.base 释放了数组的内存,但指针还是指向这块内存,只是这块内存不能被访问,所有将它们设为null
4. 入栈

5. 出栈

3. 链表
1. 初始化及是否为空


2. 入栈、出栈及取栈顶



**4.递归
见算法:递归







1. 缺点及转化





3.4 队列的表示和实现
1. 数据类型定义

2.顺序队列

front rear是表示指针,但不是指针变量,不是真正的指针





1. 初始化

2.求队列长度

3.入队

4.出栈

5.取队头元素

3. 链队列


1. 初始化

2. 销毁

3. 入队

4.出队

5. 取队头元素
