数据结构笔记(一)线性结构
数据结构
本笔记是根据数据结构与算法基础(青岛大学-王卓)归纳整理而成
本篇笔记是用markdown编写
如有侵权请联系我删除:209195687qq.com
注:转载请注明出处,谢谢!
"*"代表相对重要
类C语言补充
data(即数组名)和*data存放的是数组首地址
(ElemType*)表示强制类型转换成指针
线性表
顺序表示
顺序表的基本操作
//函数结果状态代码#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;
线性表L的初始化
status initlist_sq(sqlist &L){
L.elem=new ElemType[MAXSIZE];
if(!L.elem) exit(OVERFLOW);
L.legth=0;
return ok;
}
线性表L的销毁与清空
void destroylist(sqlist &L){
if(L.elem) delete L.elem; //释放存储地址空间(delete为c++语法)
}
void clearlist(sqlist &L){
L.length=0;
}
L的长度及是否为空
int GetLength(SqList L){
return (L.length);
}
int lsEmpty(SqList L){
if (L.length==0) return 1;
else return o;
顺序表的取值
int GetElem(SqList L,int i,ElemType &e){
if (i<1||i>L.length)
return ERROR;
e=L.elem[i-1];
return OK;
}
**顺序表的值查找(顺序查找)
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)
特点
:
随机存取
**顺序表的插入
插入位置i:1<=i<=n+1
平均查找长度
:
平均时间复杂度
:
O(n)
**顺序表的删除
插入位置i:1<=i<=n
平均查找长度
:
平均时间复杂度
:
O(n)
顺序表的总结
链式表示
概念
1.无头结点表示空表:
2.有头结点表示空表:
3.头结点的好处:
4.特点
:
顺序存取
单链表
存储结构(类型定义):
typedef struct Lnode{
ELEMTYPE data;
struct londe *next;
}Londe,*Linklist;
*Linklist表示指向这个结构块的指针
定义指针:
Linklist L / Lnode *L
基本操作(带头结点)
单链表的初始化
单链表是否为空
*单链表销毁包括头结点
一个辅助指针
*单链表清空
两个辅助指针
注:
while(L)等价于while(L!=null)
循环条件不能为while(q)
*单链表长度
一个辅助指针
**取值
一个辅助指针
1<=i<=n
while循环里跳出的情况:
p为null(i>j) j=i i<1 (即: j>i)
符合if条件的:
p为null(i>j) 或 i<1 (即: j>i)
注:此代码为伪代码
**查找
一个辅助指针
1<=i<=n
返回指针地址
同:
{
p=L->next;
while(p){
if(p->data=a)
return p;
p=p->next;
}
return p;
}
不成熟总结:
while( ){
for( )
}
等价于while( && )
返回位置序号
平均查找长度
:
平均时间复杂度
:
O(n)
**插入 (之前)
两个辅助指针
1<=i<=n+1
vs对比
平均时间复杂度
:
O(n)
注:插入和删除操作的完整过程包括查找操作,查找操作是O(n),而单个插入和删除操作只是移动指针,只是O(1)
**删除
两个辅助指针
1<=i<=n
解析:
p=L;j=0;
因为删除首元结点时要用到头结点,所以将L赋给p,而不是将L->next赋给p
while(p->next&&j<i-1):
①确保 q指向的地址不为null;如果是while(p&&j<i-1),当i=n+1时,q会指向第n+1个元素(即null),那么q->next就无意义(会报错)
while(p) p->next:p会指向第n+1个元素(即null)
while(p->next) p->next:p会指向第n个元素
②p指向第i个结点前驱的固定格式:(循环次数不重要,不需记忆)
p=L;j=0; while条件是j<i-1或j<=i-2 循环i-1次
p=L->next;j=1; while条件是j<i-1或j<=i-2 循环i-2次
p指向第i个结点的固定格式:
p=L;j=0; while条件是j<i或j<=i-1 循环i次
p=L->next;j=1; while条件是j<i或j<=i-1 循环i-1次
③if(!(p->next)||j>i-1) return error: 表示i不在
1<=i<=n
这个区间范围内,就return
结论:
p指向第i个结点前驱,while条件是j<i-1或j<=i-2
p指向第i个结点,while条件是j<i或j<=i-1
平均时间复杂度
:
O(n)
注:插入和删除操作的完整过程包括查找操作,查找操作是O(n),而单个插入和删除操作只是移动指针,只是O(1)
**建立链表
一个辅助指针
头插法
平均时间复杂度
:
O(n)
尾插法
两个辅助指针
平均时间复杂度
:
O(n)
循环链表
循环链表合并
时间复杂度
:
O(1)
双向链表
插入
4.不可以和1.或2.换
删除
不需要去找该删除结点的前驱结点
找第i个结点的时间复杂度:O(n)
下面只执行一次:O(n)
总结
双向循环链表是用空间换时间
结论:
p指向第i个结点前驱,while条件是j<i-1或j<=i-2
p指向第i个结点,while条件是j<i或j<=i-1
应用
2.1 顺序
2.2 链式
尾插法
空间复杂度:
O(1)
#include<stdio.h>
#include<stdlib.h>
#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 (Exponent1<Exponent2)
return -1;
if (Exponent1 = Exponent2)
return 0;
}
//新生成
void Attach(int a, int b, Node** temp) {
Node *p;
p = (Node*)malloc(LENGTH);
p->Coffficient = 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;
}
栈和队列
定义及特点
栈:
队:
特点:
栈:后进先出
队列:先进先出
应用
栈的表示和实现
数据类型定义
SElemType是指元素类型
顺序栈
初始化
判断是否为空及长度
清空及释放
delete s.base 释放了数组的内存,但指针还是指向这块内存,只是这块内存不能被访问,所有将它们设为null
入栈
出栈
链表
初始化及是否为空
入栈、出栈及取栈顶
**递归
见算法:递归
缺点及转化
队列的表示和实现
数据类型定义
顺序队列
front rear是表示指针,但不是指针变量,不是真正的指针
初始化
求队列长度
入队
出栈
取队头元素
链队列
初始化
销毁
入队
有头结点能把非空队列和空队列统一起来
出队
取队头元素
串、数组和广义表
串
定义
顺序存储
BF算法
n为主串,m为子串
最好情况:O(m)
最坏情况:O(n*m)
?KMP算法
数组
顺序存储
特殊矩阵的压缩存储
对称矩阵
a[i,j]的位置是[i(i-1)/2]+j-1
(为什么要减1:k从0开始存)
三角矩阵
注:下图公式都要-1
(为什么要减1:k从0开始存)
推导:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~