学习流程主要包括分为11个部分
- 绪论
- 线性表
- 栈和队列
- 递归和分治思想
- 串(KMP算法)
- 数组和广义表
- 树和二叉树
- 图
- 动态存储管理
- 查找
- 排序
二 线性表
线性表 栈和队列 串都是线性结构
常用的线性结构有:线性表,栈,队列,循环队列,数组。线性表中包括顺序表、链表等 。
线性结构:元素的非空有限集中
1.存在唯一的一个被称作“第一个”的元素;
2.存在唯一的一个被称作“最后一个”的元素;
3.除第一个之外,集合中的每一个数据元素均只有一个前驱;
4.除最后一个之外,集合中每一个数据元素均只有一个后继;
2.1 线性表类型定义
线性表:线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同的特性,即属于同一数据对象,相邻的数据元素之间存在着序偶的关系,若记为:
则表中领先于,领先于,称是的直接前驱元素,是的直接后继元素。当时,有且仅有一个直接后继;时,有且仅有一个直接前驱。
线性表中元素的个数定义为,当 时候为空表。
2.2 线性表的顺序表示
2.2.1 顺序线性表结构体
定义一个线性表(因为抽象数据类型抽象,且个人不是很用得上,此处以顺序表来实现):
定义一个动态分配顺序存储结构(因为线性表是一个灵活的数据结构,可以根据需要来增长,
和缩短.)
#include <stdio.h>
#include <stdlib.h>
//- - - - - 线性表的动态分配顺序存储结构 - - - - -
#define LIST_INIT_SIZE 10 //线性表存储空间的初始分配量
#define LIST_INCREAMENT 5 //线性表存储空间的分配增量
typedef struct {
int* elem; // 存储空间基地址
int length; // 当前长度
int listsize; // 当前分配的存储空间大小
} DynamicSeqList;
2.2.2 顺序线性表的初始化
首先,对动态线性表进行初始化操作,构造一个空的线性表(初始化线性表)List。
// 初始化线性表
void InitList(DynamicSeqList* list) {
// 构造一个空的线性表list
//malloc函数 extern void *malloc(unsigned int num_bytes);
//分配长度为num_bytes字节的内存块
list->elem = (int*)malloc(sizeof(int) * LIST_INIT_SIZE);
if (!list->elem) { // 内存分配失败
printf("内存分配失败!!!\n");
exit(1); // 以非正常方式退出(实际应为OVERFLOW)
}
list->length = 0; // 空表长度为0;
list->listsize = LIST_INIT_SIZE; // 初始内存容量
}
2.2.3 顺序线性表元素的插入
线性表元素插入操作,C语言中即是指在线性表的第个元素和第个元素之间插入一个新的元素,使长度为的线性表:
变为长度为的线性表:
所以,在第个元素之前插入一个元素时,需要将第至第(共)个元素向后移动一个位置:
// 插入元素
void ListInsert(DynamicSeqList* list, int position, int element) {
// 在线性表L中第 position 个位置之前,插入新的元素 element
// position的合法值为 1≤i≤list.length+1
if (position<1 || position>list->length + 1) {
printf("插入位置不合法");
return; //结束函数执行
}
// 如果当前存储空间已满,增加内存分配
if (list->length >= list->listsize) {
// 重新分配更大的内存空间
// realloc更改已经配置的内存空间,即更改由malloc()函数非配得内存空间大小
// realloc内存扩大有三种情况,
// 1. 当前内存段后有需要的内存空间,直接扩展,realloc返回原指针
// 2. 当前内存段后的内存空间不足,那么就使用堆中第一个能够满足要求的内存块,
// 将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置
// 3. 如果申请失败,返回NULL,此时,原来的指针仍然有效。
list->elem = (int*)realloc(list->elem, sizeof(int)*(list->listsize + LIST_INCREAMENT));
if (!list->elem) { // 内存分配失败
printf("内存分配失败!!!\n");
exit(1); // 以非正常方式退出(实际应为OVERFLOW)
}
list->listsize += LIST_INCREAMENT; //更新存储空间大小为扩容后大小
}
for (int i = list->length; i < position; i--) { //将插入位置后的元素向后移动一位
list->elem[i] = list->elem[i - 1];
}
list->elem[position - 1] = element; // 在插入位置后插入新的元素
list->length++; // 更新线性表长度
}
2.2.4 顺序线性表元素的删除
线性表元素的删除操作是使长度为的线性表:
变为长度为的线性表:
一般情况下,删除第个元素时需要将从第至第(共)个元素依次向前移动一个位置:
// 删除元素
void ListDelete(DynamicSeqList* list, int position) {
// 在线性表L中第 position 个位置删除一个元素
// position的合法值为 1≤i≤list.length+1
if (position<1 || position>list->length + 1) {
printf("删除位置不合法");
return; //结束函数执行
}
for (int i = position; i < list->length; i++) { // 将删除位置后的元素向前移动
list->elem[i - 1] = list->elem[i];
}
list->length--; //更新线性表长度
}
由上述顺序存储结构的线性表可见,顺序存储结构的线性表的某个位置上插入或者删除一个数据元素时,其时间主要耗费在移动元素上 ,而移动元素的个数取决于插入或者删除元素的位置:
假设是在第个元素之前插入一个元素的概率,则长度为的线性表中插入一个元素时所需移动元素次数的期望值为:
假设是删除第个元素的概率,则长度为的线性表中插入一个元素时所需移动元素次数的期望值为:
一般的,我们可以假设线性表的任何位置上插入或删除的概率都是相等的,即
综合上述式子,可以得到:
所以,在顺序存储结构的线性表中插入或者删除一个数据元素,平均约移动一半的元素,则算法ListInsert,和ListDelete的时间复杂度为。
下面添加一些其他的基本操作。
2.2.5 顺序线性表元素的查找
// 查找元素
int ListSearch(DynamicSeqList* list, int element) {
for (int i = 1; i < list->length; i++) { // 遍历线性表中的元素
if (list->elem[i] == element) { //如果找到指定元素
return i + 1; //返回元素在线性表中的位置
}
}
return -1; //返回-1 表示未找到指定元素
}
2.2.6 顺序线性表元素的修改
void ListModify(DynamicSeqList* list,int position, int element) {
if (position<1 || position>list->length + 1) {
printf("修改位置不合法"); //若修改位置不合法则输出错误信息
return; // 结束函数执行
}
list->elem[position - 1] = element; // 修改指定位置的元素值为新元素的值
}
2.2.7 顺序线性表的表输出
// 输出表
void ListPrint(DynamicSeqList* list) {
printf("当前线性表中的元素为:");
for (int i = 0; i < list->length; i++) { // 遍历线性表的元素
printf("list[%d]=%d ", i + 1, list->elem[i]); //输出元素的值
}
printf("\n");
}
2.2.8 顺序线性表的表删除
// 删除表
void ListDestroy(DynamicSeqList* list) { //在顺序表不再需要的时候调用
free(list->elem); // 释放存储数据元素的空间
list->elem = NULL; // 将指向数组的指针设置为空
list->length = 0; // 将线性表长度置为0
list->listsize = 0; // 将存储空间大小置为0
}
2.2.9 两个顺序表(递增)的合并 (归并排序)
// 两个顺序表(递增)的合并 (归并排序)
void MergeList(DynamicSeqList* list1, DynamicSeqList* list2, DynamicSeqList* mergeList) {
mergeList->elem = (int*)malloc(sizeof(int) * (list1->length + list2->length));
if (!mergeList->elem) { // 内存分配失败
printf("内存分配失败!!!\n");
exit(1); // 以非正常方式退出(实际应为OVERFLOW)
}
mergeList->length = 0; // 初始化列表长度
mergeList->listsize = list1->length + list2->length; //初始化存储空间大小
int i = 0, j = 0, k = 0;
// 循环遍历list1和list2,将元素按序存放如mergeList中
while (i < list1->length && j < list2->length) {
// 比较list1和list2当前位置的元素大小
if (list1->elem[i] <= list2->elem[j]) {
// 如果list1当前位置元素小于list2当前位置元素,则将list1当前位置元素放入mergeList
mergeList->elem[k++] = list1->elem[i++];
}
else {
// 如果list2当前位置元素小于list1当前位置元素,则将list2当前位置元素放入mergeList
mergeList->elem[k++] = list2->elem[j++];
}
}
// 因为归并循环的过程中 一定会出现一个顺序表已经被排完的情况。
// 将剩余的元素拷贝到合并列表中
while (i < list1->length) {
mergeList->elem[k++] = list1->elem[i++];
}
while (j < list2->length) {
mergeList->elem[k++] = list2->elem[j++];
}
mergeList->length = k; // 更新合并后列表的长度
}
2.2.10 顺序线性表基本操作的实现
int main() {
DynamicSeqList list1, list2, mergeList;
// 初始化顺序表
InitList(&list1);
InitList(&list2);
InitList(&mergeList);
// 向list1中插入一些元素
ListInsert(&list1, 1, 3);
ListInsert(&list1, 2, 5);
ListInsert(&list1, 3, 7);
ListInsert(&list1, 4, 9);
// 向list2中插入一些元素
ListInsert(&list2, 1, 4);
ListInsert(&list2, 2, 6);
ListInsert(&list2, 3, 8);
ListInsert(&list2, 4, 10);
// 输出list1和list2
printf("List1: ");
ListPrint(&list1);
printf("List2: ");
ListPrint(&list2);
// 删除list1,元素
ListDelete(&list1, 4);
ListPrint(&list1);
// 删除list2,元素
ListDelete(&list2, 4);
ListPrint(&list2);
// 查找list1元素
ListSearch(&list1, 3);
// 查找list2元素
ListSearch(&list2, 3);
// 修改list1元素
printf("修改后List1: ");
ListModify(&list1, 3, 9);
ListPrint(&list1);
// 修改list2元素
printf("修改后List2: ");
ListModify(&list2, 3, 10);
ListPrint(&list2);
// 合并表
ListMerge(&list1, &list2, &mergeList);
printf("合并后list1表为:");
ListPrint(&list1);
printf("合并后list2表为:");
ListPrint(&list2);
printf("合并后mergeList表为:");
ListPrint(&mergeList);
// 删除表
ListDestroy(&list1);
printf("删除后后list1表为:");
ListPrint(&list1);
ListDestroy(&list2);
printf("删除后后list2表为:");
ListPrint(&list2);
ListDestroy(&mergeList);
printf("删除后后mergeList表为:");
ListPrint(&mergeList);
return 0;
}
输出结果为:
List1: 当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=7 list[4]=9
List2: 当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=8 list[4]=10
当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=7
当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=8
修改后List1: 当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=9
修改后List2: 当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=10
合并后list1表为:当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=9
合并后list2表为:当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=10
合并后mergeList表为:当前线性表中的元素为:list[1]=3 list[2]=4 list[3]=5 list[4]=6 list[5]=9 list[6]=10
删除后后list1表为:当前线性表中的元素为:
删除后后list2表为:当前线性表中的元素为:
删除后后mergeList表为:当前线性表中的元素为: