文章内容来源2020、2022天勤数据结构,虽然之后可能有22、23...,但数据结构基本的内容就那些,可做参考。
前排提示,数据结构2、3部分的内容有点水,纯粹为了完成这个任务吧,可看可不看
目录
多图预警!!!天勤数据结构
基础篇
变量类型
基本类型:int(整型)、float(存小数)、char(字符串) 定义 int a; 初始化 int a, b, c = 2;
指针型 :存放变量地址。 &取地址符号 定义 int *p; 初始化 p = &a; b = *p // 把*p指向的值赋值类似b=a
NULL 不指向任何地址,值为0且指针初始化使用。
指针看不懂的可以看下面的链接。
版权声明:本文为CSDN博主「南抒一梦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39951988/article/details/87773322
构造类型:数组:相同类型的变量构成的变量集 int B[100] // 下标从0开始到99 取值 a = B[0];存值 B[99] = c;
结构体:不同类型构成的变量集 typedef struct {int a; float b; char c; ....}结构体名; 定义S s; 取值 a = s.a 存值 s.a = 1;
typedef struct 结构体名{...; struct 结构体名 *d;}结构体名
void型:定义没有返回值的函数 void Function(){ ... return;//直接跳出函数,不执行接下来的语句 ... }
控制语句
判断语句 if 循环语句 while/for continue //跳过当前循环 break //跳出循环
函数
(注意图中并未改变result的值)
(void类型没有返回值)(左C++语法 右纯C)
(int &r //引用型定义,发生改变且改变后结果重要用它)
(C++)将resul传给r然后&取到result的地址,函数方法里面就可以用r来修改result;
(C)取到result的地址传给指针形参r,*r就是result然后进行+1操作。
(int *&P //同上)
(C++)将指针p传给P然后&取到指针里的地址在*指向这个地址。
(C)将取到指针p的地址,然后传给二级指针P,通过修改*P就修改了p
逻辑结构与存储结构
逻辑结构
逻辑结构:没关系(集合)、一对一(线性表)、一对多(树)、多对多(图)
一对一:A是B的前驱节点,B是A的后继节点
存储结构
顺序结构、链式结构
顺序结构:有顺序、按地址排列,可以随机存取。(线性表、集合) 实现:数组
链式结构:存数据(A、B、C)加下一个节点的地址(ad1、ad2、adx) 实现:结构体
线性表
逻辑结构
线性表是具有相同特性数据元素的有限序列
相同特性:把同一类事物归类,方便批量处理。(方便用户,节约用户时间)【数据结构是将相同类型的数据按不同的逻辑关系来用不同方法,如树、图等都是相同类型,但是逻辑关系不一样】
有限:表中元素个数为n,n有限大,n可以为0
序列:表中元素排成一列,体现了一对一的逻辑特性(每个元素有则仅有一个前驱和一个后继)区分有序和无序
存储结构
顺序结构:int A[maxSize]; int length = 0; //maxSize 最大数 lengrh实际数据量
链式结构:
带头结点的链表判空为Head->next == null; 不带头结点为 head == null;(推荐使用带头结点的链表!!)
左一图定义了一个DLNode链表,存放了interesting类型的数据和next、prior指针,然后定义这样一个L的双链表并分配存储空间(malloc),最后指向对应结点即可。
(Head->prior == Head)
特性对比问题
顺序表插入数据即选择要插入的数组下标,然后先将从该下标到最后含数据的下标往后移一格,即要使插入的数组下标内无数据,最后就放入数据即可。类似插队,队伍中间插个人,那从那里开始就要往后退。
顺序表删除数据就是把要删除的元素直接覆盖掉,即把后面的所有元素往前移一格。类似于把中间插队的人踢出去那么就可以直接把他的位子占了,即后续的人一人向前一步,不让插队的人进来。
链表插入数据即知道要插入的前驱结点然后通过插入的结点指向前驱结点的后继结点,然后让前驱结点指向插入的结点即可。用代码就是(//p是前驱结点,q是插入结点 q->next = p->next; p->next->q;)
链表删除数据即直接把要删除结点的前驱结点指向删除结点的后继结点即可。(p->next = q->next;free(q); //红色线即为删除)
顺序表中插入和删除元素可能会导致移动大量的连带操作(插入和删除操作发生在表尾位置例外),而链表不会。
取元素顺序表比链表简单。
1.顺序表可能会比链表浪费更多的空间。(设置了maxsize,maxsize满了就一样ps.顺序表需要一次性建立,不能分批次建立,因为可能没有顺序的存储空间来存储)
2.单个顺序表元素的存储空间利用率高于链表,因为多了一个指针。
移动次数计算和静态链表
顺序表插入元素的移动次数:1.任意位置插入元素的概率为:p=1/(1+n);2.在i位置(i的取值:0~n)之前插入元素,需要移动n+i次;3.总移动次数等差数列求和即(n+1)(n+0)/2=(n+1)n/2;4.平均移动元素的个数:概率×总移动次数即n/2.
顺序表删除元素的移动次数:1.删除元素概率为:p=1/n;2.在i位置(i∈[0, n-1])处删除元素,需要要移动n-1-i次;3.总移动次数为(n-1)n/2;4.平均移动元素的个数(n-1)/2.(ps.要理解记忆,题目可能有坑)
静态和动态链表:静态:某种存储结构一次性分配;动态:多次分配。
(考点一般给定静态链表,对存储细节考察)
插入和删除
链表的插入删除
单链表内部插入删除前文一提到过就不赘述了。
带头结点的头部插入即内部插入。不带头结点的头部插入是先将插入的结点指向head指向的结点,然后更新head位置。(带头结点用代码来看很一致,都是内部插入,但是不带头结点就需要进行分类讨论)
带头结点的链表头部删除同内部删除。不带头结点的需要head = head -> next; free(p);即先讲头指针指向头部的下一个结点,然后释放删除结点的空间。
双链表内部插入即先使原本为空的s的next的指针域指向p->next的结点,然后令s 的前驱指针即prior指向p,然后接下来两步就不一一说明了。(所有插入的优先要考虑的就是不能丢失数据,出现断链的情况。只要不出现断链就可以任意调换程序语句执行次序。)
双链表内部删除
从图中可以看出只要知道要删除结点的位置就可以了,不需要知道上图中p结点位置。第一步是将s->prior即p然后
p->next = s->next;第二步是s->next 即s的下一个结点然后令下一个结点的前驱指针指向s的前驱结点即可。第三步释放s空间。
顺序表的插入删除
int List[maxSize] = {1, 2, 3, 4, ..., n};
int length = n;
int insertElem(int List[], int &length, int p, int e){
if(p>0 || p>length || length==maxSize) return 0; // 判断插入是否合法和数据是否溢出
for(int i = length-1; i >= p; --i) //移动p及之后的所有数据
List[i+1] = List[i];
List[p] = e; //在p出放值
++length; //!!!更新数组长度
return 1; //成功标志
}
int deleteElem(int List[], int &length, int p, int &e){
if(p<0 || p>length-1) return 0; //length=0不用判断,已经囊括了,因为p>0-1;
e = List[p]; //取出删除元素的值,所以用引用型变量(使用规则参见函数变量)
for(int i = p; i<length-1; ++i)
List[i] = List[i+1];
--length;
return 1;
}
建表
链表建表:(头插尾插法详解:https://blog.csdn.net/qq_41028985/article/details/82859199)
#include <iostream>
using namespace std;
typedef struct LNode{
int data;
struct LNode* next;
}LNode;
void CreateLinkListR(LNode *&head){ //尾插法
head = (LNode*)malloc(sizeof(LNode));
head->next = NULL;
LNode *p = NULL, *r = head;
int n;
std::cin>>n; //scanf("%d", &n);
for (int i=0; i<n; i++){
p = (LNode*)malloc(sizeof(LNode));
p->next = NULL; //null要大写!!
cin>>p->data;
p->next = r->next; // 可以省去,为了统一成中间插入
r->next = p;
r = p;
}
}
void CreateLinkListH(LNode *&head){ //头插法
head = (LNode*)malloc(sizeof(LNode));
head->next = NULL;
LNode *p = NULL;
int n;
cin>>n;
for (int i=0; i<n;i++){
p = (LNode*)malloc(sizeof(LNode));
cin>>p->data;
p->next = head->next; //此处应指向head->next,使最后一项为null。也可直接等于null
head->next = p;
}
}
int main(int argc, char *argv[])
{
LNode *head;
CreateLinkListR(head);
cout << head->next->data;
return 0;
}
真题演练:(我是用尾插法做的 自行检验头插法)
#include <iostream>
using namespace std;
typedef struct LNode{
char data;
struct LNode* next;
}LNode;
void CreateAndFindSame(LNode *&head){
int n;
char elem;
LNode *p,*r;
head = (LNode*)malloc(sizeof(LNode));
head->next = NULL;
r = head;
cin>>n;
for (int i=0; i<n; i++){
cin>>elem;
p = head->next;
while (p){
if (p->data == elem)
break;
p = p->next;
}
if (p == NULL){
p = (LNode*)malloc(sizeof(LNode));
p->data = elem;
p->next = NULL;
r->next = p;
r = p;
}
}
}
void Show(struct LNode* L) { //遍历链表值
cout << "链表遍历:" ;
while (L->next) {
cout << L->next->data << " ";
L = L->next;
}
cout << endl;
}
int main(int argc, char *argv[])
{
LNode *head;
CreateAndFindSame(head);
Show(head);
return 0;
}
(记录:突然有点搞不清->的意思。p申请了一个空间,那p就是结点,然后p的next域里是存放下一个结点的位置,即p->next = r)
逆置
顺序表逆置:left为所要逆置的元素的最左端,right即最右端。i<j即到达逆置的所有元素的中间即结束(i<j是奇数个逆置元素,i=j是偶数个逆置元素,其原理都是一样 都可以用i<j)。先用中间变量temp存储a[i]防止数据丢失,然后a[i]=a[j] 将元素换位置,然后令a[j]=temp,然后i++,j--向中间靠拢,最后完成逆置。
链表逆置:p是从p结点之后开始逆置,中间是p到q个元素。
首先用中间变量存储p->next防止结点丢失,然后p-next=t->next使 t 指向的这个结点“删除”(看不懂可以看前面的链表删除操作),然后t->next=q->next使 t 链接到q的下一个结点,为下一步的添加做准备(看不懂可以看前面的链表添加操作),然后p->next=t 添加到链表中,至此完成一次逆置,然后当p->next == q时交换完成。整个过程就是把 p 之后的元素一个个搬运到后面,不断的更新p的位置,最后p指向的下一结点就是q了。
真题:
1.
i<j是防止k超过n/2.
2.
先转置k-1个元素,使这几个元素使转置的 再使这几个元素转置到n-k-1~n-1的位置上,完成按原序移动。
3.
转置前p个元素,再转置p之后的元素,然后转置所有元素即完成要求。
取最值
最小值即min > a[i]
最小值即min > p->data
1.找出最大值结点。2.“删除”最大值结点。3.插入最大值结点
归并
归并:有序的线性表,结果也是有序的
线性表合并
链表尾插法(正序)即1 2 3 4 5 6
链表头插法(逆序)即 6 5 4 3 2 1
划分
以某个元素为标准,把顺序表的元素分为左右两个部分,使其左边大于右边或右边大于左边,或大于小于规定值(枢轴)
1.给一个顺序表,以第一个元素为枢轴,将顺序表划分为左右两部分,使左边所有元素小于枢轴,而右边大于等于枢轴,并且枢轴在左右元素之间。
void partition(int arr[], int n)
{
int temp;
int i=0, j=n-1;
temp = arr[i] // 取第一个为枢轴
while (i < j)
{
while (i < j && arr[j] >= temp) j--; //i < j 是每次循环的时候判断,防止i >= j的情况
if (i < j) //当上层循环结束可能出现i = j 的情况 如 1,2,3。 j最后等于0跳出循环
{
arr[i] = arr[j];
i++; //i++与++i应该等同的
}
while (i < j && arr[i] < temp) i++; //逻辑正好相反
if (i < j)
{
arr[j] = arr[i];
j--;
}
}
arr[i] = temp;
}
2.用非顺序表(或顺序表非第一个数)中的数做枢轴。
3用顺序表中的任意一个值做枢轴。
交换位置即可。
数据结构(2)笔记链接
https://blog.csdn.net/Z_timer/article/details/106457448
给自己其他文章小小引流一下😇
数据结构之图论概念大全
https://blog.csdn.net/Z_timer/article/details/109330916