一、数据结构绪论
概念:数据的组织方式,施加在数据上的一组操作,(增删改查等)。
1.1数据:能被计算机识别存储处理的符号集合。
数据分为数值类型和非数值类型
数值:int a,123,float b;等我们熟知的变量和常量都是数值数据。
非数值:mp3,光盘,磁带,电影视频。
1.2数据对象:多个数据对象构成了数据
例如:1班,2班,3班 都属于数据对象,构成班级属于数据。
1.3数据元素:多个数据元素构成了数据对象
例如:1班有27个(学生)数据元素。
1.4数据项:数据元素的最小组成单位。
例如:数据元素人有两条腿,有耳朵,有嘴,这些都是组成数据元素的最小单位。
数据>数据对象>数据元素>数据项
数据元素是组成数据的基本单位,数据项是数据的最小单位。
1.5数据结构体分为逻辑结构和物理结构体
顺序存储:逻辑相邻的元素物理上也相邻。
链式存储:逻辑相邻的元素物理上不一定相邻。
散列存储:哈希存储,数据元素的存储和哈希函数和关键字有关(了解)。
索引存储:在存储数据元素时建立一个索引表,以便快速查找。
1.6顺序存储和链式存储的区别
1、顺序存储必须占用连续的内存空间。
2、链式存储内存空间连续与否均可。
3、链式存储时节点体积较小,可以充分利用内存碎片。
4、相同节点个数的顺序存储和链式存储,链式存储占用内存较多。
5、链式存储和顺序存储都有优缺点,不能单纯的认为链式或者顺序存储较好。
二、线性表
例如:顺序表,顺序栈,顺序队列,顺序串,单链表,双链表,循环链表都属于线性表。
1、第一个元素没有前驱最后一个元素没有后继。
2、中间 的元素都有唯一的前驱和唯一的后继,元素之间一对一关系。
三、顺序表(sequence)
3.1 概念
1、数据元素之间采用顺序存储的方式。
2、数据元素之间占用连续的内存空间。
3、采用数组存储顺序表。
3.2 实现方式
1、借助数组来存储顺序表。
2、数组长度固定
3、顺序表长度可变。
4、顺序表长度<=数组长度
3.3顺序表的组成
1、顺序表借助数组来存储,封装结构体时需要封装数组。
2、顺序表长度不固定,所以还需要封装计数器,统计顺序表元素个数。
#define MAX 15//数组长度是15
typedef struct
{
int data[MAX];//存储顺序表的数组
int len;//统计顺序表长度的变量
}list,*Plist;
MAX:预估顺序表的存储长度最大值
data:存储顺便表的数组。
len:统计顺序表元素个数。
list:结构体普通类型。
Plist:结构体指针类型。
3.4有关顺序表的操作(功能函数)
创建顺序表
目标:存储班级所有学生信息,然后对班级同学进程增删改查,去重,排序,查找等。详见
3.5顺序表的优缺点
1、定位插入删除,移动大量元素,时间复杂度O(n).
2、定位查找修改,不需要移动元素,时间复杂度O(1)
3、冒泡排序时间复杂度O(n*n),改进的也是O(n*n).。
4、插入删除不方便,因为需要移动大量元素,并不是时间复杂度是O(n)
5、修改查找方便,因为不需要移动元素。
3.6时间复杂度概念
时间复杂度分析是以最坏的情况分析。
T(n) = O(f(n));
T:时间复杂度的表示方法
n:问题的规模
O:渐进符号
f(n):代码总执行次数
int fun(int n,int a[])
{
int i,j,t;//1次
for(i = 1;i<n;i++)//n-1次
{
for(j = 0;j<n;j++)//n*(n-1)次
{
if(a[j]>a[j+1])//n*(n-1)次
{
t = a[j];//n*(n-1)次
a[j] = a[j+1];//n*(n-1)次
a[j+1]=t;//n*(n-1)次
}
}
}
return 0;//1次
}
T(n) = O(f(n))=O(5*n*(n-1)+(n-1)+1+1)=O(5*n*n-4*n+1)
O(5*n*n-4*n+1)---保留最高阶---> O(5*n*n)---系数化为1--->O(n*n)
四、链表
4.1目的
1、插入删除不需要移动任何节点(元素)。
2、不需要预估存储空间大小,长度动态增长或减小。
4.2 概念
1、采用链式存储的线性表。
4.3 链表的种类
五、单向链表
5.1 单向链表的概念
1、每个节点都有2部分组成,一部分是存储数据,一部分是存储地址。
2、节点描述:存储数据的叫数据域,存储地址的叫指针域。
3、单链表节点存在第0号节点(头节点),不存储数据,只存储节点个数len。
5.2单向链表节点结构体格式
typedef struct zhangzhen
{
union{
int data;//用于正常节点,存储数据
int len;//用于头节点,计算节点个数。
};
struct zhangzhen *next;//指针域,存储下一个节点地址
}link,*Plink;
5.3 单链表的相关操作
1>创建头节点
2>头插法创建单链表
3>尾插法创建单链表
4>遍历单链表
5> 任意位置插入
6>任意位置删除
7>任意位置查找
8>任意位置修改
9>头删
10>尾删
11>按值删除
12>按值修改
13>按值查找返回地址
14>链表逆置
15>去重
注意事项:删除重复的节点后,不需要移动 j ,继续将i与 j->next比较。
16> 销毁
5.4单向链表的优缺点
优点:
1、插入删除不需要移动元素,只需要修改指针。
2、节点之间不连续,可以充分利用内存碎片存储数据。
3、不需要预估存储空间。
4、节点个数不受限制。
不足:
1、修改查找需要循环遍历节点。
2、相同长度的线性表,与顺序表相比单链表占用空间较多。
3、只能从头往后访问,不能反向访问。
总结:
1、单链表插入删除(头插,头删)时间复杂度O(1),查找修改,访问时间复杂度O(n)。
5.5 单链表实现学生管理系统
六、双向链表
6.1作用
1》单向链表只能单向访问链表,而双向链表可以正序或倒序访问查找链表
2》单向链表不能自我删除,每次都需要找到要删除结点的前一个结点进行删除,引入双向链表可以实现自我删除,找到要删除的结点,让要删除的前驱结点链接到要删除结点的后继结点,然后删除结点即可。
6.2 双向链表的相关操作(功能函数)
1>双向链表的创建
功能:创建一个双向链表头结点。
参数:void
返回值:申请到的头结点的地址指针
思路:在堆区申请一个头结点的空间大小,对其进行初始化,数据域len为0,指针域为NULL
注意:判断申请到的空间是否合法
2>头插
功能:在头结点的后面插入一个结点
返回值:成功1 失败0
参数:双向链表、要插入的元素
思路:1、给要插入元素申请结点
2、将要插入的结点插在头结点的后面
3、链表长度+1
注意:判断接收到的双向链表是否合法
1、没有1号节点时
第一步、新节点p后指针置空
第二步、新节点前指针指向头节点
第三步、头节点后指针指向新节点
2、有1号节点时
第一步、新节点后指针指向1号节点
第二步、新节点前指针指向头节点
第三步、1号节点前指针指向新节点
第四步、头节点后指针指向新节点。
3>尾插
1、没有1号节点时
第一步、新节点p后指针置空
第二步、新节点前指针指向头节点
第三步、头节点后指针指向新节点
2、有1号节点时
第一步、新节点后指针指向空
第二步、新节点前指针指向 t
第三步、t的后指针指向新节点p
4>双向链表的遍历
功能:共头到尾输出双链表【也可以从尾到头输出双链表】
返回值:无
参数:双链表
思路:只要双链表不空,从头结点开始一个接着一个的输出
5>任意位置插入
功能:在双链表长度范围之内的任意位置,插入一个结点
返回值:成功1 失败0 int
参数:双链表、要插入的数据、指定的位置
思路:知道要插入的位置
6>任意位置删除
7>链表任意位置查找、修改,按值查找、修改,排序,销毁等操作与单链表操作相同(同单链表)
七、循环链表
7.1概念
1、首尾相连的单链表或双链表。
7.2链表的相关操作(功能函数)
链表的格式同不循环链表
对于功能函数来说,与不循环链表区别有以下,其余功能函数没有区别
创建链表时p->next=p;
Plink create()
{
Plink p = malloc(sizeof(Link));
if(NULL==p)
{
printf("申请失败\n");
return NULL;
}
p->len = 0;
p->next = p;//指向自己循环单链表
return p;
}
尾插
尾删
Plink t =L;
for(i = 0;i<L->len-1;i++)
{
t = t->next;
}
Plink Q = t->next;//保留要删除的节点
t->next= L;
free(Q);
Q =NULL;
L->len--;
销毁
PLink t=L->next,Q=t;
while(t!=L){
t=t->next;
free(Q);
Q=t;
}
free(L);//最后再释放头节点
printf("释放成功\n");