408 数据结构线性表算法

news/2024/11/15 15:46:08/文章来源:https://www.cnblogs.com/lingchuanfeng/p/18328821

第一章 线性表

定义 :线性表是具有 相同数据类型 的n(n>=0)个数据元素的 有限序列

线性表的表示 :若用L命名,表示:L=(a1,a2,a3,a4,a5,……,an)

线性表的逻辑特性

  • a1:唯一的表头元素
  • an:唯一的表尾元素
  • 除去a1:每个元素有且仅有一个直接前驱
  • 除去an:每个元素有且仅有一个直接后继

线性表的特点

  • 表中元素是有限个
  • 表中元素具有逻辑上的顺序性,各个元素有先后次序
  • 表中元素都是数据元素,每一个元素都是单个元素
  • 表中元素的数据类型都相同
  • 表中每个元素占用相同大小的存储空间
  • 表中元素具有抽象性。线性表 仅讨论元素间的逻辑关系 ,不讨论元素的具体内容

线性表、顺序表、链表

  • 线性表是 逻辑结构 ,表示一对一的相邻关系
  • 顺序表是 采用顺序存储 对线性表的实现,是指存储(物理)结构
  • 链表是采用 链式存储 对线性表的实现,是指存储(物理)结构

线性表的基本操作

image-20240110155942296

1.1 顺序表

把线性表中所有元素按照逻辑顺序,依次存储到从指定位置开始的一块 连续存储空间 ,线性表的顺序存储叫作顺序表。

特点:

  • 第一个元素的存储位置就是指定的存储位置

  • 第i+1个元素的存储位置紧接在第i个元素的存储位置后面

  • 逻辑相邻的两个元素物理也相邻

  • 顺序表可以实现随机存取

  • 存储密度高(不用额外存储指示信息)

  • 逻辑相邻在物理上也相邻,插删需要移动大量元素

image-20240110155606045

1.1.1 顺序表结构

线性表的顺序存储(即顺序表)的存储类型描述

一维数组静态分配

#define MaxSize 128		//定义顺序表的最大长度
typedef int ElemType;	//理解成给int起个别名,以后用ElemType代表int,体现抽象数据类型
//顺序表的顺序存储结构
typedef struct {ElemType data[MaxSize];	//顺序表的主体用来存储数据元素int length;				//顺序的当前长度
}SqList;			//同样给顺序表struct类型起个别名,以后用SqList代表顺序表struct

静态存储的特点

  • 数组的大小空间已经固定
  • 空间满时,再加入新数据会导致溢出

一维数组的动态分配

#define InitSize 16		//顺序表的初始容量大小
#define MaxSize 128		//顺序表的最大容量大小typedef int ElemType;
typedef struct {ElemType* data;		//动态分配数组的指针int length;			//顺序表的当前长度int capacity;		//记录当前容量
}DySqList;

动态存储的特点

  • 在执行过程中根据需要,动态分配。
  • 空间满时,可以开辟另外一块更大空间,达到扩充目的

动态分配不是链式存储,分配的空间依然是连续的,依然采用随机存取方式

动态存储的实现

与静态存储方式的实现基本一致,只是需要手动申请空间以及释放空间(这里给出代码不再细讲)

//初始化
void initList(DySqList& L) {L.data = (ElemType*)malloc(InitSize * sizeof(ElemType));if (L.data == NULL) {exit(-1);	//内存分配失败}L.length = 0;	//顺序表长置0L.capacity = InitSize;	//更开始申请时顺序表的容量即为初始化容量大小
}
//扩容
void expansion(DySqList& L) {L.capacity += (L.capacity >> 1);if (L.capacity >= MaxSize) {L.capacity = MaxSize;exit(-1);}//先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域L.data = (ElemType*)realloc(L.data, L.capacity * sizeof(ElemType));if (!L.data) {exit(-1);//分配内存失败}
}//创建顺序表
void createList(DySqList& L) {ElemType x;scanf("%d", &x);int i = 0;while (x != 999) {if (i >= L.capacity) {expansion(L);}//*(L.data + i) = x;L.data[i++] = x;scanf("%d", &x);}L.length = i;
}//插入操作。在表L中第pos个位置上插入指定元素e
bool insertList(DySqList& L, int pos, ElemType e) {//如果顺序表已满返回falseif (L.length >= L.capacity) {exit(-1);}//检查插入位置pos是否合法,不合法返回falseelse if (pos<1 || pos>L.length + 1) {return false;}//将最后一个元素开始直到第pos个位置处的元素依次后移for (int i = L.length - 1; i >= pos - 1; i--) {L.data[i + 1] = L.data[i];}L.data[pos - 1] = e;	//将待插入的元素插入到第pos个位置上L.length++;				//维持表长正确,表长+1return true;
}bool deleteList(DySqList& L, int pos, ElemType& e) {//如果顺序表为空,返回falseif (L.length == 0) {return false;}//检查pos位置的合法性else if (pos<1 || pos>L.length) {return false;}else {//将待删除元素用e接收e = L.data[pos - 1];//将第pos+1个位置处的元素直到最后一个元素依次前移for (int i = pos; i < L.length; i++) {L.data[i - 1] = L.data[i];}//维持顺序表长度的正确性L.length--;return true;}
}//按值查找操作。在表L中查找具有给定关键字值的元素,若存在返回第一个值为e的所在位置,不存在返回0
int locateElem(DySqList L, ElemType e) {int ans = 0;	//用来记录最终返回的结果for (int i = 0; i < L.length; i++) {if (L.data[i] == e) {ans = i + 1;	//返回所在位置,下标要+1,如果找到退出查找break;}}return ans;
}//按位查找操作。获取表L第pos个位置的元素的值
ElemType getElem(DySqList L, int pos) {//检查pos位置的合法性,不合法直接退出程序if (pos <1 || pos>L.length) {exit(0);}return L.data[pos - 1];
}//判空操作。若L为空表,则返回true,否则返回false
bool isEmpty(DySqList L) {return L.length == 0;
}//销毁顺序表
void destroyList(DySqList& L) {free(L.data);	//释放内存空间L.length = 0;L.capacity = 0;L.data = NULL;
}void printList(DySqList L) {for (int i = 0; i < L.length; i++) {printf("%d ", L.data[i]);}printf("\n");
}

1.1.2 顺序表的初始化

void initList(SqList& L) {//对数据进行初始化,防止不当使用时出现脏数据for (int i = 0; i < MaxSize; i++) {L.data[i] = 0;}L.length = 0;	//表长初始化为0
}

1.1.3 在指定位置插入元素

image-20240119112433425

在第二个位置插入元素100

image-20240119112532334

从最后一个位置到第pos个位置上的元素依次后移一位,再将100插到第pos个位置上,表长+1

//插入操作。在表L中第pos个位置上插入指定元素e
bool insertList(SqList& L, int pos, ElemType e);

算法步骤

  1. 检查顺序表是否已满,如果已满返回false表示插入失败
  2. 检查pos位置是否合法(1<=pos<=L.length+1,如果pos不合法返回false,表示插入失败
  3. 从最后一个位置开始直到第pos个位置上的元素依次后移
  4. 将待插入的元素插入到pos个位置处
  5. 维持表长的正确性(即将表长加一)返回true

算法实现

bool insertList(SqList& L, int pos, ElemType e) {//如果顺序表已满返回falseif (L.length >= MaxSize) {return false;}//检查插入位置pos是否合法,不合法返回falseelse if (pos<1 || pos>L.length + 1) {return false;}else {//将最后一个元素开始直到第pos个位置处的元素依次后移for (int i = L.length - 1; i >= pos - 1; i--) {L.data[i + 1] = L.data[i];}L.data[pos - 1] = e;	//将待插入的元素插入到第pos个位置上L.length++;				//维持表长正确,表长+1return true;			}
}

复杂度分析

image-20240110165613113

区别顺序表的位序(1开始)和数组下标(0开始)

1.1.4 删除指定位置的元素

image-20240119112801151

删除第二个位置的元素值

image-20240119114116949

从pos后一个位置到顺序表中最后一个位置依次前移,最后表长-1。此时原表中的最后一个元素还存在在表中,但是表长-1后我们可以理解顺序表中已经被删除了要删除的元素了。或者最后可以手动将最后一个元素值设为0。

//删除操作。删除表L中第pos个位置的元素,并用e返回删除元素的值
bool deleteList(SqList& L, int pos, ElemType& e);

算法步骤

  1. 如果表为空返回false
  2. 检查pos位置是否合法(1<=pos<=L.length),如果不合法返回false
  3. 用e接收被删除的元素值
  4. 将第pos+1位置上的元素直到表的最后元素依次前移
  5. 维持表长的正确性(表长减一)返回true

代码实现

bool deleteList(SqList& L, int pos, ElemType& e) {//如果顺序表为空,返回falseif (L.length == 0) {return false;}//检查pos位置的合法性else if (pos<1 || pos>L.length) {return false;}else {//将待删除元素用e接收e = L.data[pos - 1];//将第pos+1个位置处的元素直到最后一个元素依次前移for (int i = pos; i < L.length; i++) {L.data[i - 1] = L.data[i];}//维持顺序表长度的正确性L.length--;return true;}
}

复杂度分析

image-20240110214226053

1.1.5 查找指定位置的元素(顺序查找)

//按值查找操作,在表L中查找具有给定关键字值的元素,若存在返回第一个值为e的所在位置,不存在返回0
int locateElem(SqList L, ElemType e);

算法实现

//按值查找操作,在表L中查找具有给定关键字值的元素,若存在返回第一个值为e的所在位置,不存在返回0
int locateElem(SqList L, ElemType e) {int ans = 0;	//用来记录最终返回的结果for (int i = 0; i < L.length; i++) {if (L.data[i] == e) {ans = i + 1;	//返回所在位置,下标要+1,如果找到退出查找break;}}return ans;
}

复杂度分析

image-20240110215639105

1.1.6 查找元素所在位置

ElemType getElem(SqList L, int pos) {//检查pos位置的合法性,不合法直接退出程序if (pos <1 || pos>L.length) {exit(0);}return L.data[pos - 1];
}

时间复杂度:O(1)。顺序表支持随机存取

1.1.7 顺序表的判空

bool isEmpty(SqList L) {return L.length == 0;
}

1.1.8 销毁顺序表

void destroyList(SqList& L) {for (int i = 0; i < L.length; i++) {L.data[i] = 0;}L.length = 0;
}

静态数组的内存由编译器在栈上管理,而不需要手动释放

1.1.9 顺序表的打印

void printList(SqList L){for (int i = 0; i < L.length; i++) {printf("%d ", L.data[i]);}printf("\n");
}

1.1.10 刷题

01.从顺序表中删除具有最小值的元素( 假设唯一) 并由函数返回被删元素的值。空出的位
置由最后一个元素填补,若顺序表为空,则显示出错信息并退出运行。

遍历顺序表,找到值最小的元素下标

image-20240119114230558

将最后一个元素放到最小值所在下标处。

image-20240119114404598

算法思路

遍历顺序表,找到值最小的元素所在下标,将最后一个元素放到最小值所在下标处。

算法步骤

  1. 判断表是否为空,如果为空返回false
  2. 定义两个变量min及minIndex分别用来表示最小值和最小值所在位置下标,默认0位置处元素是最小值
  3. 从第二个元素开始遍历顺序表如果当前遍历的元素值比min小,则将当前元素赋值给min,当前下标赋值给minIndex
  4. 遍历结束后,minIndex位置即为最小值所在位置下标,用最后一个元素覆盖最小值
  5. 维持表长正确性(表长减一),返回true

算法实现

bool delMin(SqList& L) {//空表直接返回falseif (L.length == 0) {printf("表空无法操作\n");return false;}int min = L.data[0];//记录最小值int minIndex = 0;	//记录最小值所在位置下标for (int i = 1; i < L.length; i++) {if (L.data[i] < min) {min = L.data[i];minIndex = i;}}L.data[minIndex] = L.data[L.length - 1];	//用最后一个位置元素覆盖被删除元素L.length--;	//维持表长正确性return true;
}

02.设计一个高效算法,将顺序表L的所有元素逆置,要求算法的空间复杂度为0(1)。

算法思路

将第一个元素与最后一个进行交换,第二个与倒数第二个交换,依次类推,直到交换表长的一半次即可

算法步骤

  1. 记录应交换的次数(即L.length/2次)
  2. 进行L.length/2次元素交换即可

算法实现

//实现方法1
void reverseList(SqList& L) {int midCount = L.length / 2;	//记录应交换的次数int i = 0;while (i < midCount){//首尾交换元素int temp = L.data[i];L.data[i] = L.data[L.length - 1 - i];L.data[L.length - 1 - i] = temp;i++;}
}
//实现方法2
void reverseList2(SqList& L) {int low = 0, high = L.length - 1;//当前两个"指针"分别指向第一个元素和最后一个元素//当前半部分指针和下半部分指针没有相遇时交换low指针和high指针所指向的元素while (low < high) {int temp = L.data[low];L.data[low] = L.data[high];L.data[high] = temp;low++;high--;}
}

03.对长度为n的顺序表L,编写一个时间复杂度为0(n)、空间复杂度为0(1)的算法,该算法删除线性表中所有值为x的数据元素。

算法思路

边遍历边统计边移动。遍历顺序表,统计值为x的个数count,将不是值为x的元素前移count位,维护表长正确性,将表长减count即可

算法实现

void delX(SqList& L,ElemType x) {int count = 0;	//用来记录值为x的元素个数for (int i = 0; i < L.length; i++) {if (L.data[i] == x) {count++;}else {L.data[i - count] = L.data[i];	//如果当前元素值不为x,将其前移count位}}L.length -= count;	//维护表长
}

04.从顺序表中删除其值在给定值s与t之间(包含s和t, 要求s < t)的所有元素,若s或t不合理或顺序表为空,则显示出错信息并退出运行。

算法思路1

与03题思路相同,只不过此时在遍历顺序表时统计的是介于s与t之间的元素个数count,将值不在s、t之间的元素前移count位,并维护表长。

算法实现1

void delS2T(SqList& L, ElemType s, ElemType t) {if (L.length == 0) {printf("顺序表为空,无法操作\n");exit(0);}else if (s >= t) {printf("s、t输入不合法\n");exit(0);}else {int count = 0;	//用来统计介于s与t之间的元素个数for (int i = 0; i < L.length; i++) {if (L.data[i] >= s && L.data[i] <= t) {count++;}else {L.data[i - count] = L.data[i];}}L.length -= count;}
}

算法思路2

利用双指针,i作为遍历指针,j每次指向满足不在s、t之间要插入的位置,遍历完成后,j即为删除介于s、t之间元素后的新表长。

算法实现2

void delS2T_2(SqList& L, ElemType s, ElemType t) {if (L.length == 0) {printf("顺序表为空,无法操作\n");exit(0);}else if (s >= t) {printf("s、t输入不合法\n");exit(0);}else {int i = 0, j = 0;//i作为遍历指针,j每次指向满足不在s、t之间要插入的位置while (i < L.length) {if (L.data[i]<s || L.data[i]>t) {L.data[j++] = L.data[i];	//将需要保留的元素插入到j所指位置}i++;}L.length = j;	//j即为新表长}
}

05.从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,若s或t不合理或顺序表为空,则显示出错信息并退出运行.

算法思路1

找到第一个大于等于s的元素以及大于t的元素,将第一个比t大的元素直到最后一个元素前移到第一个大于等于s的位置及其后续位置。

算法实现1

void delStoT(SqList& L, ElemType s, ElemType t) {if (L.length == 0) {printf("顺序表为空,无法操作\n");exit(0);}else if (s >= t) {printf("s、t输入不合法\n");exit(0);}else {int i = 0, j;//查找第一个>=s的元素下标while (i < L.length && L.data[i] < s) {i++;}//如果不存在大于等于s的元素直接退出,没必要在删除了if (i >= L.length) {exit(0);}//找第一个>t的元素下标for (j = i; j < L.length && L.data[j] <= t; j++);//第一个比t大的元素直到最后一个元素前移到第一个大于等于s的位置及其后续位置。while (j < L.length) {L.data[i++] = L.data[j++];}L.length = i;//此时i即为新表长}
}

算法思路2

方法1中我们在进行查找第一个s和第一个大于t的元素时使用的顺序查找,时间复杂度为O(n),为此我们可以利用折半查找来降低查找的时间复杂度使之变为O(logn),当然后续移动的操作不变,由于在顺序表删除元素的时间复杂度扔为O(n),故整体时间复杂度依然为O(n),只不过对方法1的查找操作进行了代码优化。

//用二分法找第一个比s大的元素所在位置下标
int findBigEqualLeft(SqList L, ElemType target) {int left = 0, right = L.length - 1;int ans = -1;while (left <= right) {int mid = (left + right) >> 1;if (L.data[mid] >= target) {ans = mid;right = mid - 1;}else {left = mid + 1;}}return ans;
}
//用二分法找第一个比t大的元素所在位置下标
int findSmallEqualRight(SqList L, ElemType target) {int left = 0, right = L.length - 1;int ans = -1;while (left <= right) {int mid = (left + right) >> 1;if (L.data[mid] <= target) {ans = mid;left = mid + 1;}else {right = mid - 1;}}return (ans == -1 ? ans : ans + 1);
}void delStoT_3(SqList& L, ElemType s, ElemType t) {if (L.length == 0) {printf("顺序表为空,无法操作\n");exit(0);}else if (s >= t) {printf("s、t输入不合法\n");exit(0);}else {int findBigSFirst = findBigEqualLeft(L, s);if (findBigSFirst == -1) {return;}int findBigTFirst = findSmallEqualRight(L, t);if (findBigTFirst == -1) {return;}//没有比t大的元素if (findBigTFirst >= L.length) {L.length -= (findBigTFirst - findBigSFirst);return;}else {for (int i = findBigTFirst; i < L.length; i++) {L.data[findBigSFirst++] = L.data[i];}L.length = findBigSFirst;}}
}

算法思路3

利用双指针,i为工作指针,j为满足不介于s、t之间的元素所指位置,当遍历到的当前元素<s,i和j指针同时后移,当元素值介于s、t之间时,只移动工作指针i,当遍历到大于t时,将此时及后面的所有元素覆盖从i开始的元素值,最后j即为表长。

算法实现3

void delStoT_2(SqList& L, ElemType s, ElemType t) {if (L.length == 0) {printf("顺序表为空,无法操作\n");exit(0);}else if (s >= t) {printf("s、t输入不合法\n");exit(0);}else {int i = 0, j = 0;	//i作为遍历指针,j指向满足要保存的元素位置for (; i < L.length; i++) {if (L.data[i] < s) {j++;}else if (L.data[i] > t) {L.data[j++] = L.data[i];}}L.length = j;}
}

06.从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

算法思路1

从第二个元素依次和前一个元素比较值是否相等,如果相等记录此时出现相同元素的个数count,如果不等将当前元素前移count位,维护表长的正确性。

算法实现1

void delRepeat(SqList& L) {int count = 0; //统计重复元素的个数for (int i = 1; i < L.length; i++) {if (L.data[i] == L.data[i - 1]) {count++;}else {L.data[i - count] = L.data[i];	//将不重复的元素前移count位}}L.length -= count;
}

算法思路2

利用双指针,i和j初始都指针第二个元素,因为第一个元素自己的话一定不重复,i作为工作指针遍历顺序表,j每次指向满足不重复元素应在的位置,如果i和i-1处的值相同,只移动i指针,如果不等说明其是应保留的非重复元素将其放到j所指位置,最终j即为表长。

算法实现2

void delRepeat2(SqList &L) {int i = 1, j = 1;while (i < L.length) {if (L.data[i] != L.data[i - 1]) {L.data[j++] = L.data[i];}i++;}L.length = j;
}

07.将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。

算法思路

同时遍历LA和LB表中的元素,比较当前两个表中的哪个元素小,谁小将其拷贝到LC中,如果相等规定先拷贝LA中的,当然也可以规定相等时先拷贝LB中的。

算法实现

bool mergeList(SqList LA, SqList LB,SqList &LC) {if (LA.length + LB.length > MaxSize) {return false;}int i = 0, j = 0, k = 0;	//i,j,k分别作为LA,LB,LC的遍历指针while (i < LA.length && j < LB.length) {if (LA.data[i] <= LB.data[j]) {		//谁小将其拷贝到LC中,相等默认先拷贝LA中的LC.data[k++] = LA.data[i++];}else {LC.data[k++] = LB.data[j++];}}//如果LA,LB有一个还没遍历完,将其剩下元素拷贝到LC中。两个while只会执行一个while (i < LA.length) {		LC.data[k++] = LA.data[i++];}while (j < LB.length) {LC.data[k++] = LB.data[j++];}LC.length = LA.length + LB.length;return true;
}

08.已知在一维数组A[m + n]中依次存放两个线性表(a1,a2,a3,...,am)和(b1,b2,b3,...,bn).编写一
个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,...,bn)放在(a1,a2,a3,...,am)的前面。

算法思路

先逆置前m个元素,在逆置m-m+n位置的元素,最后将整个表逆置即可。

算法实现

//将顺序表的第start到end位置上的元素进行逆置
void inverList(SqList& L, int start, int end) { int low = start - 1, high = end - 1;while (low < high) {int temp = L.data[low];L.data[low] = L.data[high];L.data[high] = temp;low++;high--;}
}
void exchangeListElem(SqList& L, int m, int n) {inverList(L, 1, m);	//逆置前m个元素inverList(L,m + 1, m + n);	//逆置后n个元素inverList(L, 1, m + n);		//将顺序表整体进行逆置
}

09.查找值为x的元素,若找到,啧将其与后继元素位置相互交换,若找不到,则将其插入表中并使表中元素仍然递增有序。

void findExchangeInsert(SqList& L, ElemType x) {int low = 0, high = L.length - 1;int ans = -1;while (low <= high) {int mid = (low + high) / 2;if (L.data[mid] < x) {low = mid + 1;}else if (L.data[mid] > x) {high = mid - 1;}else {ans = mid;break;}}//证明已经找到了if (ans != -1 && ans != L.length - 1) {int temp = L.data[ans];L.data[ans] = L.data[ans + 1];L.data[ans + 1] = temp;}//如果没有找到if (ans == -1) {for (int i = L.length - 1; i >= low; i--) {L.data[i + 1] = L.data[i];}L.data[low] = x;L.length++;}
}

10.将顺序表元素左移p个位置,即将数据由(X0,X1....,Xn-1)变换成(Xp,Xp+1...,Xn-1,X0,X1,....,Xp-1)。

void inverseList(ElemType data[], int start, int end) {int low = start - 1;int high = end - 1;while (low < high) {int temp = data[low];data[low] = data[high];data[high] = temp;low++;high--;}
}void leftRemove(ElemType data[], int p, int size) {if (p > size || p < 1) {return;}inverseList(data, 1, p);inverseList(data, p + 1, size);inverseList(data, 1, size);
}

11.求两个序列的中位数,例如,S1={11,13,15,17,19},S2 = {2,4,6,8,20},则S1和S2的中位数为11。

//思路1,利用归并排序
int findMid1(SqList LA, SqList LB) {SqList LC;initList(LC);if (LA.length + LB.length >= MAXSIZE) {exit(-1);}int i = 0, j = 0, k = 0;while (i < LA.length && j < LB.length) {if (LA.data[i] <= LB.data[j]) {LC.data[k++] = LA.data[i++];}else {LC.data[k++] = LB.data[j++];}}//当前LA还没有遍历完while (i < LA.length) {LC.data[k++] = LA.data[i++];}//当前LB还没有遍历完while (j < LB.length) {LC.data[k++] = LB.data[j++];}LC.length = LA.length + LB.length;return LC.data[LA.length - 1];
}
//思路2,同样利用归并排序思路,但不使用额外的空间
int findMid(SqList L1, SqList L2) {int i = 0, j = 0;int count = 1;int size = L1.length;while (i < L1.length && j < L2.length && count < size) {if (L1.data[i] < L2.data[j]) {i++;}else {j++;}count++;}return L1.data[i] < L2.data[j] ? L1.data[i] : L2.data[j];
}
//思路3,利用二分查找的思路
int findMid2(SqList L1, SqList L2) {int s1 = 0, s2 = 0, d1 = L1.length - 1, d2 = L2.length - 1;while (s1 != d1 || s2 != d2) {int m1 = (s1 + d1) / 2;int m2 = (s2 + d2) / 2;if (L1.data[m1] == L2.data[m2]) {return L1.data[m1];}if (L1.data[m1] < L2.data[m2]) {//证明是奇数个if ((s1 + d1) % 2 == 0) {s1 = m1;d2 = m2;}else {s1 = m1 + 1;d2 = m2;}}else {if ((s2 + d2) % 2 == 0) {s2 = m2;d1 = m1;}else {s2 = m2 + 1;d1 = m1;}}}return L1.data[s1] < L2.data[s2] ? L1.data[s1] : L2.data[s2];
}

12.顺序表L中的数据元素递增有序。试写一个算法,将x插入到顺序表适当的位置上,以保持该表的有序性。

/*思路1.利用顺序查找1.找到第一个大于等于x元素所在位置i2.将最后一个元素开始一直到第i个元素后移3.将元素e插入到i位置上
*/
void insertXtoList1(SqList& L, ElemType x) {if (L.length >= MAXSIZE) {return;}int i = 0;while (i < L.length && L.data[i] < x) {i++;}for (int j = L.length - 1; j >= i; j--) {L.data[j + 1] = L.data[j];}L.data[i] = x;L.length++;
}
/*思路2利用二分查找1.用二分查找找到第一个大于x元素所在位置2.将最后一个元素依次到第一个大于x的元素后移3.空出的位置插入元素x
*/void insertXtoList2(SqList& L, ElemType x) {if (L.length >= MAXSIZE) {return;}int low = 0, high = L.length - 1;while (low <= high) {int mid = (low + high) / 2;if (L.data[mid] <= x) {low = mid + 1;}else {high = mid - 1;}}for (int i = L.length - 1; i >= low; i--) {L.data[i + 1] = L.data[i];}L.data[low] = x;L.length++;
}

13. 已知顺序表中每个元素为不相等的整数。设计算法把所有的奇数移到所有的偶数前面(要求时间最少,辅助空间最少)

void partitonList(SqList& L) {int i = 0, j = L.length - 1;while (i < j) {while (i < j&& L.data[i] % 2 == 1) {i++;}while (i<j && L.data[j] % 2 == 0) {j--;}int temp = L.data[i];L.data[i] = L.data[j];L.data[j] = temp;i++;j--;}
}

14.请写一个算法,在顺序表中查找指定的数据,查找成功后将该记录放到顺序表的最前面,而把其他记录后退一个位置

void searchValue(SqList& L, ElemType x) {int pos = 0;for (int i = 0; i < L.length; i++) {if (x == L.data[i]) {pos = i;break;}}for (int k = pos - 1; k >= 0; k--) {L.data[k + 1] = L.data[k];}L.data[0] = x;
}

15.给你一个顺序表L ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

int singleNumber(SqList L) {int ans = 0;for (int i = 0; i < L.length; i++) {ans ^= L.data[i];}return ans;
}

16.给你一个顺序表L,只有可以将其划分为三个和相等的 非空 部分时才返回 true,否则返回 false

示例 1:

输入:L = [0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1

示例 2:

输入:L = [0,2,1,-6,6,7,9,-1,2,0,1]
输出:false

示例 3:

输入:L = [3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4
bool canThreePartsEqualSum(SqList L) {int sum = 0;for (int i = 0; i < L.length; i++) {sum += L.data[i];}if (sum % 3 != 0) {return false;}int partSum = sum / 3;int part[2] = { 0 };int low = 0, high = L.length - 1;while (low < L.length-2) {part[0] += L.data[low];if (part[0] == partSum) {break;}low++;}if (low >= L.length - 2) {return false;}while (high - low >= 2) {part[1] += L.data[high];if (part[1] == partSum) {break;}high--;}if (part[1] == partSum) {return true;}else {return false;}
}

17.给定一个顺序表L和一个整数目标值target,请你设计一个时间复杂度尽可能高效的算法,在该顺序表中找出是否存在有两个元素之和等于target,若存在返回true否则返回false。假设顺序表L中的元素值均在(0-1000之间).

//思路1暴力求解时间复杂度O(n²)
bool twoSum(SqList L, int target) {for (int i = 0; i < L.length; i++) {for (int j = 0; j < L.length; j++) {if (i != j && L.data[i] + L.data[j] == target) {return true;}}}return false;
}
//思路2用空间换时间,时间复杂度O(n)bool twoSum2(SqList L, int target) {int arr[1001] = {0};for (int i = 0; i < L.length; i++) {arr[L.data[i]] = 1;}for (int i = 0; i < L.length; i++) {int ans = target - L.data[i];if (arr[ans] == 1) {return true;}}return false;
}

18.给你一个顺序表L,请你选择L的两个不同下标 i 和 j,使 (L.data[i])*(L.data[j])取得最大值。请你计算并返回该式的最大值。

int max(int a, int b) {return a >= b ? a : b;
}int maxProduct(SqList L) {if (L.length < 2) {return 0;}int firstmax = max(L.data[0], L.data[1]);int secondmax = (firstmax == L.data[0]) ? L.data[1] : L.data[0];for (int i = 2; i < L.length; i++) {if (L.data[i] > firstmax) {secondmax = firstmax;firstmax = L.data[i];}else if (L.data[i] > secondmax) {secondmax = L.data[i];}}return firstmax * secondmax;
}

19. 给你一个目标值target,你需要编写算法使得目标值等于target的放在中间,目标值小于target的放在左侧,目标值大于target的放在右侧。

void dividList(SqList& L, int target){int smallPart = -1;int bigPart = L.length;int i = 0;while (i < bigPart) {if (L.data[i] < target) {swap(L, ++smallPart, i++);}else if (L.data[i] > target) {swap(L, --bigPart, i);}else {i++;}}
}

1.2单链表

1.2.1定义

通过一组 任意的存储单元 来存储线性表中的数据元素,是线性表的 链式存储。

单链表节点类型描述

  1. 因为逻辑相邻不一定物理相邻,所以需要额外的指针来存储后继信息。
  2. 单链表节点的组成
    1. data:数据域,存放数据元素
    2. next:指针域,存放后继节点的地址
  3. 代码定义
typedef struct {ElemType data;	//数据域LNode* next;	//指针域,指向后继结点
}LNode,*LinkedList;
typedef struct {ElemType data;LNode* next;
}LNode;typedef struct {LNode* head;	//头指针int length;		//记录表长
}LinkedList;

单链表特点

  1. 解决了顺序表插删需要大量移动元素的缺点
  2. 引入了额外的指针域,浪费了空间
  3. 单链表是非随机存取的存储结构,查找需要从头遍历

“头指针”与“头结点”

`` image-20240112111525514

1.2.2头插法创建单链表(不带头结点)

void createListByHead(LinkedList& L) {ElemType x;scanf("%d", &x);while (x != 999) {LNode* node = (LNode*)malloc(sizeof(LNode));	//申请结点内存空间node->data = x;	//为新结点赋值if (L == NULL) {node->next = NULL;	//如果是插入的第一个结点。用头插法的话最后他是尾结点将其next指空}else {node->next = L;	//如果不是插入的第一个结点,将当前结点直接之前的头指针}L = node;	//更新最新的结点作为头指针scanf("%d", &x);}
}

1.2.3尾插法创建单链表(不带头结点)

void createListByTail(LinkedList& L) {LNode* tail = NULL;	//尾指针ElemType x;scanf("%d", &x);while (x != 999) {LNode* node = (LNode*)malloc(sizeof(LNode));node->data = x;node->next = NULL;if (L == NULL) {	//如果是第一个结点,头尾都指向该结点L = node;tail = node;}else {tail->next = node;	//如果不是第一个结点,尾指针的next指向该结点tail = node;		//当前新添加结点作为尾结点}scanf("%d", &x);}
}

1.2.4返回第pos个位置上的结点(不带头结点)

LNode* getNode(LinkedList L, int pos) {//位置不对返回NULLif (pos < 0) {return NULL;}int i = 1;LNode* p = L;while (p != NULL && i < pos) {p = p->next;i++;}return p;
}

1.2.5按值查找结点(不带头结点)

LNode* locateElem(LinkedList L, ElemType e)
{LNode* p = L;while (p!= NULL&&p->data!=e) {p = p->next;}return p;
}

1.2.6在第pos个位置插入元素e(不带头结点)

void insertElem(LinkedList& L, int pos, ElemType e) {//检查pos位置的合法性if (pos < 1 || pos>length(L) + 1) {return;}1//创建新结点LNode* node = (LNode*)malloc(sizeof(LNode));node->data = e;//如果是在第一个位置插入元素,新结点作为头指针if (pos == 1) {node->next = L;L = node;}else {LNode *pre = getNode(L, pos - 1);	//找到插入位置的前一个结点node->next = pre->next;				pre->next = node;}
}

1.2.7删除第pos个位置的元素结点,并用e返回被删除结点的元素值(不带头结点)

void deleteElem(LinkedList& L, int pos, ElemType& e) {//检查pos位置的合法性if (pos < 1 || pos>length(L)) {return;}//查找被删除结点LNode *removed = getNode(L, pos);//如果要删除第一个结点元素,直接将要删除的下一个元素作为头指针if (pos == 1) {L = removed->next;}else {LNode* pre = getNode(L, pos - 1);	//找到被删除元素结点的前一个pre->next = removed->next;		}e = removed->data;	//将要删除结点的值赋值给e接收free(removed);		//释放被删除元素的空间内存
}

1.2.8求表长(不带头结点)

int length(LinkedList L) {LNode* p = L;if (p == NULL) {return 0;}int ans = 0;while (p != NULL) {p = p->next;ans++;}return ans;
}

1.2.9单链表的基本操作(带头结点)

typedef int ElemType;typedef struct LNode {ElemType data;LNode* next;
}LNode, * LinkedList;
//初始化单链表
void initLinkedList(LinkedList& L) {L = (LNode*)malloc(sizeof(LNode));	//申请头结点if (L) {L->next = NULL;L->data = 0;						//头结点的数据域保存链表长度}
}
//头插法创建单链表
void createLinkedListWithHead(LinkedList& L) {ElemType x;scanf("%d", &x);while (x != 999) {LNode* cur = (LNode*)malloc(sizeof(LNode));	//申请新结点空间cur->data = x;						cur->next = L->next;	//新结点的next指向头结点的nextL->next = cur;			//头结点的next指向当前新结点L->data += 1;			//链表长度+1scanf("%d", &x);}
}
//尾插法创建单链表
void createLinkedListWithTail(LinkedList& L) {ElemType x;LNode* tail = L;scanf("%d", &x);while (x != 999) {LNode* cur = (LNode*)malloc(sizeof(LNode));cur->data = x;		cur->next = NULL;tail->next = cur;	//尾指针的next每次指向最新结点tail = cur;			//最新结点成为尾指针L->data += 1;		//表长+1scanf("%d", &x);}}
//返回第pos个位置上的结点
LNode* getNode(LinkedList L, int pos) {//检查pos是否合法,data是链表的长度if (pos<0 || pos>L->data) {return NULL;}int i = 0;	LNode* p = L;while (p && i < pos){p = p->next;i++;}return p;
}
//在第pos个位置插入元素e
void insertElem(LinkedList& L, int pos, ElemType e){if (pos<1 || pos>L->data + 1) {return;}LNode* cur = (LNode*)malloc(sizeof(LNode));cur->data = e;LNode * pre = getNode(L,pos - 1);	//获得要插入位置的前一个位置结点cur->next = pre->next;				//新结点的next指向前一个结点的nextpre->next = cur;					//前一个结点的next指向当前结点L->data += 1;						//表长+1
}
//删除第pos个位置的元素结点,并用e返回被删除结点的元素值
void deleteElem(LinkedList& L, int pos, ElemType& e){//检查pos是否合法if (pos<1 || pos>L->data) {return;}LNode* removed = getNode(L, pos);	//记录要删除元素LNode* pre = getNode(L, pos - 1);	//记录要删除元素的前一个元素结点e = removed->data;					//被删除元素用e接收返回pre->next = removed->next;			//被删除的前一个结点指向其后一个结点L->data--;							//表长-1free(removed);						//释放删除结点的空间
}
//打印链表
void printLinkedList(LinkedList L) {LNode* p = L->next;		//第一个数据元素是头结点的后继结点元素while (p) {printf("%d->", p->data);p = p->next;}printf("\n");
}

1.2.10刷题

01.设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。

//递归
void delX(LinkedList& L,ElemType x) {if (L == NULL) {	//终止条件return;}if (L->data == x) {		//如果是被删除元素结点LNode* p = L;		//防止断链L = L->next;		//记录被删除元素的下一个结点,free(p);			//释放被删除元素空间delX(L, x);			//递归下一个结点}else {					//如果当前元素的值不是被删除元素,递归下一个元素delX(L->next, x);}
}
//非递归
void delX2(LinkedList& L, ElemType x) {if (L == NULL) {return;}LNode* removed;//用来记录被删除元素结点while (L!=NULL&&L->data == x) {	//如果链表还没有遍历完找到第一个值不为x结点,同时将遍历过程中值为x的结点都删除removed = L;//如果当前结点值为x记录当前结点为待删除结点L = L->next;	//L后移free(removed);	//释放被删除结点的空间}if (L == NULL) {//如果L已经为NULL,证明结点值都为x且已被删除直接返回return;}LNode* p = L->next;		//此时L一定头指针,节点元素值不为xLNode* pre = L;			//用来指向不是值为x的元素的最后一个结点while (p){//如果当前节点的值为x,removed记录待删除节点,p后移,pre指向待删除结点的后继节点,释放removed空间if (p->data == x) {removed = p;p = p->next;pre->next = removed->next;free(removed);}//如果当前节点的值不为x,pre和p同时后移else {pre = pre->next;p = p->next;}}pre->next = NULL;	//删除结束后pre指向最后一个结点,将其next指空
}

02.在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点
不唯一,试编写算法以实现.上述操作。

void delX2(LinkedList& L, ElemType x) {	LNode* p = L->next;//p作为遍历指针从首结点开始if (p == NULL) {//如果链表为空,返回return;}LNode* removed;//用来记录被删除元素结点LNode* pre = L;//pre始终指向最后一个值不为x的结点while (p) {//如果当前节点的值为x,removed记录待删除节点,p后移,pre指向待删除结点的后继节点,释放removed空间if (p->data == x) {removed = p;p = p->next;pre->next = removed->next;free(removed);}//如果当前节点的值不为x,pre和p同时后移else {pre = pre->next;p = p->next;}}pre->next = NULL;//删除结束后pre指向最后一个结点,将其next指空
}

03.设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。

//递归
void inverPrintList(LinkedList L) {LNode* p = L->next;		//从第一个首节点开始if (p == NULL) {		//终止条件return;}inverPrintList(p);		//递归printf("%d->", p->data);	
}
//非递归
void inverPrintList2(LinkedList L) {//申请动态数组用来保存链表中的元素值ElemType* set = (ElemType*)malloc(sizeof(ElemType) * L->data);int i = 0;LNode* p = L->next;while (p) {set[i++] = p->data;p = p->next;}for (int j = i - 1; j >= 0; j--) {printf("%d->", set[j]);}printf("\n");
}

04.试编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。

void delMin(LinkedList& L) {if (L->next == NULL) {return;}LNode* p = L->next;	//从首节点开始LNode* pre = L;		//始终是p的前一个位置的指针LNode* min = p;		//记录最小值指针,假设初始第一个元素就是最小值LNode* minpre = L;	//记录最小值的前一个元素指针while (p) {if (p->data < min->data) {	//如果当前元素的值比最小值小min = p;				//最小值指针指向当前结点minpre = pre;			//最小值的前一个指向pre}//每次p和pre都后移p = p->next;pre = pre->next;}minpre->next = min->next;	//删除min结点free(min);	//释放空间
}

05.试编写算法将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为0(1)。

void reverse(LinkedList& L) {LNode* cur = L->next;LNode* next = NULL;L->next = NULL;while (cur) {next = cur->next;cur->next = L->next;L->next = cur;cur = next;}
}

06.有一个带头结点的单链表L,设计一个算法使其元素递增有序。

void sortListAsc(LinkedList& L) {//如果链表为空,或者链表只有一个结点直接返回不需要排序if (L->next == NULL || L->next->next == NULL) {return;}//从第二个结点开始LNode* p = L->next->next;L->next->next = NULL;while (p != NULL) {LNode* next = p->next;	//记录一下p的后继防止断链LNode* head = L->next;	//head作为每次已排好序的部分链表中的首节点同时作为遍历指针LNode* pre = L;	//遍历指针head的前一个结点while (head != NULL && p->data > head->data) {//如果不满足插入位置pre和head后移pre = pre->next;	head = head->next;}p->next = head;	//在head和pre之间插入ppre->next = p;p = next;	//p后移重新遍历未进行排序的结点操作}
}

07.设在一个带表头结点的单链表中所有元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素的元素(若存在)。

void delSToT(LinkedList& L, ElemType s, ElemType t){if (L->next == NULL) {return;}LNode* p = L->next;		//遍历指针从首节点开始LNode* pre = L;			//始终指向不是删除元素的最后位置while (p) {if (p->data >= s && p->data <= t) {	//如果是被删除元素LNode* removed = p;	//记录被删除元素p = p->next;		//后移pre->next = p;		//最后一个非删除元素指向当前删除元素的后继free(removed);		//释放删除结点空间}else {			//如果不在删除范围,p和pre后移p = p->next;pre = pre->next;}}
}

08.给定两个单链表,编写算法找出两个链表的公共结点。

LNode* findCommon(LinkedList L1, LinkedList L2){int len1 = length(L1);int len2 = length(L2);LNode* longhead = len1 > len2 ? L1 : L2;LNode* shorthead = longhead == L1 ? L2 : L1;//int diff = abs(len1 - len2);int diff = len1 - len2 > 0 ? len1 - len2 : len2 - len1;while (diff > 0) {longhead = longhead->next;diff--;}while (longhead) {if (longhead == shorthead) {return longhead;}longhead = longhead->next;shorthead = shorthead->next;}return NULL;
}

09.给定一个带表头结点的单链表,设head为头指针,结点结构为(data, next),data为整型元素,next为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(要求 : 不允许使用数组作为辅助空间)。

LNode* delMin2(LinkedList& L) {if (L->next == NULL) {return L;}LNode* p = L->next;	//从首节点开始LNode* pre = L;		//始终是p的前一个位置的指针LNode* min = p;		//记录最小值指针LNode* minpre = L;	//记录最小值的前一个元素指针while (p) {if (p->data < min->data) {min = p;minpre = pre;}p = p->next;pre = pre->next;}minpre->next = min->next;return min;
}void sortListAndDelete(LinkedList& L) {if (L->next == NULL) {return;}LNode *min = delMin2(L);printf("%d->", min->data);free(min);if (L->next != NULL) {sortListAndDelete(L);}
}

10. 将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A表中含有原表中序号为奇数的元素, 而B表中含有原表中序号为偶数的元素,且保持其相对顺序不变。

LinkedList resolveList(LinkedList& LA) {LNode* p = LA->next;if (p == NULL) {return NULL;}LinkedList LB = (LNode*)malloc(sizeof(LNode));	//申请LB的头结点LNode* tailA = LA;	//LA的尾指针LNode* tailB = LB;	//LB的尾指针int i = 1;			//用来标记遍历的结点的序号是奇序号还是偶序号while (p) {//奇数时if (i % 2 == 1) {tailA->next = p;tailA = p;}//偶数时else {tailB->next = p;tailB = p;}p = p->next;i++;}	tailA->next = NULL;		//最后不要忘了把LA和LB的尾指针的next指向空tailB->next = NULL;return LB;
}

11.设C={a1,b1,a2, b2,...,an bn}为线性表,采用带头结点的单链表存放,设计一个就地算
法,将其拆分为两个线性表,使得A= {a1, a2,...,an}, B= {bn,..., b2, b1}.

void resolveList2(LinkedList LC, LinkedList& LA, LinkedList& LB) {if (LC->next == NULL) {return;}LA =(LNode*) malloc(sizeof(LNode));	//LA的头结点LB =(LNode*) malloc(sizeof(LNode));	//LB的头结点LA->next = NULL;LB->next = NULL;LNode* p = LC->next;LNode* tail = LA;int i = 1;while (p) {LNode* next = p->next;	//记录p的后继防止发生断链if (i % 2 == 1) {		//当序号为奇数的时候,尾插法插到LA中tail->next = p;tail = p;}else {			//当序号为偶数时,用头插法插到LB中p->next = LB->next;LB->next = p;}i++;p = next;}tail->next = NULL;
}

12.在一个递增有序的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素, 例如(7, 10, 10, 21, 30, 42, 42, 42, 51, 70)将变为(7, 10, 21, 30, 42, 51, 70)。

//递归
void delSame(LinkedList& L) {//递归终止条件if (L == NULL||L->next == NULL) {return;}//从第二个节点开始与前一个结点元素值比较,pre为前一个cur为当前遍历结点LNode* pre = L;	LNode* cur = L->next;//如果当前结点值等于前一个删除当前节点并释放空间if (pre->data == cur->data) {pre->next = cur->next;free(cur);delSame(L);}else {delSame(L->next);}
}
//非递归
void delSame2(LinkedList& L) {if (L == NULL || L->next == NULL) {return;}LNode* pre = L;LNode* cur = L->next;while (cur){if (cur->data == pre->data) {LNode* removed = cur;pre->next = cur->next;cur = cur->next;free(removed);}else {pre = pre->next;cur = cur->next;}}
}

13.假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两
个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结
点存放归并后的单链表。

LinkedList inverseMergeList(LinkedList LA, LinkedList LB) {if (LA == NULL && LB == NULL) {return NULL;}//给新链表创建个头结点LinkedList L = (LNode*)malloc(sizeof(LNode));L->next = NULL;LNode* p = LA;	//p是LA的遍历指针LNode* q = LB;	//q是LB的遍历指针while (p && q) {LNode* pnext = p->next;	//记录p的后继防止断链LNode* qnext = q->next;//记录q的后继防止断链//谁小谁先用头插法插到新链表中if (p->data <= q->data) {p->next = L->next;L->next = p;p = pnext;}else {q->next = L->next;L->next = q;q = qnext;}}//下面两个while只会执行一个,谁还没遍历完再将剩余的插到新链表中while (p) {LNode* pnext = p->next;p->next = L->next;L->next = p;p = pnext;}while (q) {LNode* qnext = q->next;q->next = L->next;L->next = q;q = qnext;}//我们建立一个头结点是为了方便操作,用完释放掉LNode* head = L->next;free(L);return head;
}

14.设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的
公共元素产生单链表C,要求不破坏A、B的结点。

LinkedList findCommonElem(LinkedList LA, LinkedList LB) {LinkedList L = (LNode*)malloc(sizeof(LNode));	//申请头结点L->next = NULL;	LNode* tail = L;	//尾指针LNode* p = LA->next;	//LA和LB的遍历指针LNode* q = LB->next;while (p && q) {if (p->data == q->data) {	//值相等创建新结点执行尾插法LNode* node = (LNode*)malloc(sizeof(LNode));node->data = p->data;tail->next = node;tail = node;p = p->next;q = q->next;}else if (p->data < q->data) {	//谁小谁后移p = p->next;}else {q = q->next;}}	tail->next = NULL;		//最后将尾指针指空return L;
}

15.已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于A链表中。


void unionList(LinkedList& LA, LinkedList LB) {LNode* head = (LNode *)malloc(sizeof(LNode));	//头结点head->next = NULL;LNode* tail = head;		//尾指针LNode* p = LA;			//p和q分别为LA和LB的遍历指针LNode* q = LB;while (p && q) {if (p->data == q->data) {tail->next = p;tail = p;p = p->next;q = q->next;}else if (p->data < q->data) {p = p->next;}else {q = q->next;}}tail->next = NULL;LA = head->next;free(head);
}

16.两个整数序列A=a1,a2, a3,...,am和B=b1,b2,b3,....,bn已经存入两个单链表中,设计一
个算法,判断序列B是否是序列A的连续子序列。

bool isChildList(LinkedList LA, LinkedList LB) {LNode* p = LA;		//LA的遍历指针LNode* q = LB;		//LB的遍历指针LNode* start = p;	//每次记录p开始的结点位置while (p && q) {if (p->data == q->data) {	//相等一块后移p = p->next;q = q->next;}else {start = start->next;	//不相等start后移p = start;				//p重新来到start的位置q = LB;					//q重头开始遍历}}if(q==NULL){return true;}else {return false;}
}
//思路2
bool isSubsequence(LinkedList LA, LinkedList LB) {LNode* p = LA;	//p和q分别作为LA和LB的遍历指针LNode* q = LB;	while (p && q) {LNode* pnext = p->next;while(p &&q &&p->data == q->data) {p = p->next;q = q->next;}if (q == NULL) {return true;}else if (p != NULL) {p = pnext;q = LB;}}return false;
}

17. 判断循环双链表是否对称

bool isSymmetry(DLinkedList L) {DNode* p = L->next;	//p刚开始在首节点处DNode* q = L->prior;	//q来到最后一个位置处while (p != q && q->next != p) {if (p->data == q->data) {p = p->next;q = q->prior;}else {return false;}}return true;
}

18.有两个循环单链表,链表头指针分别为h1和h2, .编写一个函数将链表h2链接到链表
h1之后,要求链接后的链表仍保持循环链表形式。

LinkedList connectList(LinkedList& h1, LinkedList& h2) {LNode* p = h1;	//他俩都是为了找到最后一个节点LNode* q = h2;while (p->next != h1) {p = p->next;}while (q->next != h2) {q = q->next;}p->next = h2;q->next = h1;return h1;
}

19.设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有pre(前驱指针)、
data ( 数据)和next (后继指针)域外,还有一个访问频度域freq.在链表被启用
前,其值均初始化为零。每当在链表中进行一次Locate(L,X)运算时,令元素值为x
的结点中freq 域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排
列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表
头.试编写符合.上述要求的Locate (L,x)运算的算法,该运算为函数过程,返回找到
结点的地址,类型为指针型。

DNode* Locate(DLinkedList& L, ElemType x) {DNode* p = L->next;while (p && p->data != x) {p = p->next;}if (p == NULL) {return NULL;}else {p->freq++;if (p->prior == L || p->freq < p->prior->freq) {return p;}if (p->next != NULL) {p->next->prior = p->prior;}p->prior->next = p->next;DNode* pre = p->prior;//从p的前面结点开始和p的freq比较while (pre !=L && p->freq >= pre->freq) {pre = pre->prior;}p->next = pre->next;pre->next->prior = p;p->prior = pre;pre->next = p;return p;}
}

20.判断链表是否有环

//判断单链表是否有环
bool hasCircle(LinkedList L) {LNode* s = L, * q = L;	//s和q分别是快慢指针while (q != NULL && q->next != NULL) {s = s->next;q = q->next->next;if (s == q) {return true;}}return false;
}

21.给你单链表的头指针L和两个整数 left和 right,其中 left <= right 。请你反转从位置 left 到位置 right的链表节点,返回反转后的链表(其中1<=left<=right<=链表长度)。

void reverseListByAssignPosition(LinkedList& L, int left, int right) {if (L == NULL || left > right) {return;}LNode* dummyHead = (LNode *)malloc(sizeof(LNode));dummyHead->next = L;int i = 1;LNode* pre = dummyHead;LNode* p = L;while (i < left) {p = p->next;pre = pre->next;i++;}LNode* front = NULL;LNode* cur = p;for (int j = i; j <= right; j++) {LNode* next = p->next;p->next = front;front = p;p = next;}pre->next = front;cur->next = p;L = dummyHead->next;
}

22.两个整数序列A=a1, a2, a3,am和B=b1, b2, b3, bn,已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列。

bool isSubsequence(LinkedList LA, LinkedList LB) {LNode* p = LA;	//p和q分别作为LA和LB的遍历指针LNode* q = LB;while (p && q) {LNode* pnext = p->next;while (p && q && p->data == q->data) {p = p->next;q = q->next;}if (q == NULL) {return true;}else if (p != NULL) {p = pnext;q = LB;}}return false;
}

23.设将n(n>1)个整数存放到不带头结点的单链表L中,设计算法将L中保存的序列循环
右移k (0<k<n)个位置。例如,若k=1,则将链表{0,1,2, 3}变为{3,0,1,2}。

要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度。

void rightRemoveKposition(LinkedList &L,int k) {LNode* p = L;LNode* q = L;int step = k;//记录步数,让p先走step步while (k > 0) {p = p->next;k--;}while (p->next) {p = p->next;q = q->next;}p->next = L;L = q->next;q->next = NULL
}
//思路2
void rightRemoveKposition2(LinkedList& L, int k) {LNode* p = L;int len = 1;while (p->next != NULL) {p = p->next;len++;}p->next = L;p = L;int i = 1;while (i < len - k) {p = p->next;i++;}L = p->next;p->next = NULL;
}

24.设有一个长度n(n为偶数)的不带头结点的单链表,且结点值都大于0,设计算法求这
个单链表的最大孪生和。孪生和定义为一个结点值与其孪生结点值之和,对于第i个结
点(从0开始),其孪生结点为第n-i-1个结点。

要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
3)说明你的算法的时间复杂度和空间复杂度。

int getmax(int a, int b) {return a >= b ? a : b;
}int getBigSum(LinkedList L) {LNode* s = L, * q = L;while (q) {s = s->next;q = q->next->next;}LNode* pre = NULL;while (s) {LNode* next = s->next;s->next = pre;pre = s;s = next;}int max = 0;s = L;while (pre) {max = getmax(max, pre->data + s->data);pre = pre->next;s = s->next;}return max;
}

25.将单链表L进行划分,且保持其相对次序 。例如L = {1,5,4,4,2,5,3,2,6,5,3,4}.且target=4,最终LA{1,2,3,2,3,4,4,4,5,5,6,5}

void resolveList2(LinkedList &L,int target) {LNode* p = L;//申请LA,LB,LC的头结点为了方便操作的统一LNode* headA = (LNode*)(malloc(sizeof(LNode)));LNode* headB = (LNode*)(malloc(sizeof(LNode)));LNode* headC = (LNode*)(malloc(sizeof(LNode)));//LA,LB,LC的尾指针LNode* tailA = headA;LNode* tailB = headB;LNode* tailC = headC;while (p) {if (p->data < target) {tailA->next = p;tailA = p;}else if (p->data == target) {tailB->next = p;tailB = p;}else {tailC->next = p;tailC = p;}p = p->next;}tailA->next = headB->next;tailB->next = headC->next;tailC->next = NULL;L = headA->next;free(headA);free(headB);free(headC);
}

26.设计算法删除单链表上值一样的多余结点的算法

void delSame3(LinkedList& L) {LNode* p = L;LNode* tail = L;	//tail作为已经不重复元素的尾指针LNode* q = L->next;		//从第二个元素开始依次和前面不重复的元素比看是否会有重复L->next = NULL;while (q != NULL) {p = L;LNode* next = q->next;	//记录后继防止断链while (p) {if (q->data == p->data) {free(q);break;}p = p->next;}//说明在已经不重复的链表中没有找到与q重复的元素,将q连到不重复链表去if (p == NULL) {tail->next = q;tail = q;tail->next = NULL;}q = next;}
}

27.已知两个链表A和B分别表示两个集合。编制函数,求A与B的交集,并存放于C链表中,假设集合A与集合B中无重复元素

void unionList(LinkedList LA, LinkedList LB, LinkedList& LC) {LNode* p = LA;LNode* q = LB;LC = NULL;LNode* tail = NULL;while (p != NULL) {q = LB;LNode* next = p->next;while (q != NULL) {if (q->data == p->data) {if (LC == NULL) {LC = p;tail = p;}else {tail->next = p;tail = p;tail->next = NULL;}}q = q->next;}p = next;}
}

28.[2015统考真题]用带头结点单链表保存m个整数,结点的结构为[data][next], 且 |data|≤
n(n为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的结点,
仅保留第一次出现的结点而删除其余绝对值相等的结点。例如,若L={21,-15,-15,-7,15}的头结点单链表
删除完之后变为L={21,-15,-7};

int fabs(int a) {return a >= 0 ? a : -a;
}
//以空间换时间(时间复杂度O(m),空间复杂度O(1))
void delFabSameNum(LinkedList &L,int n) {int* arr = (ElemType*)malloc(sizeof(ElemType) * (n + 1));LNode* p = L->next;LNode* tail = L;while (p) {int ans = fabs(p->data);LNode* next = p->next;if (arr[ans] == 1) {free(p);}else {tail->next = p;tail = p;tail->next = NULL;arr[ans] = 1;}p = next;}
}//思路2,暴力法,时间复杂度O(m²),空间复杂度O(1)
void delFabSameNum(LinkedList& L) {if (L->next == NULL || L->next->next == NULL) {return;}LNode* p = L->next->next;LNode* tail = L->next;LNode* q = L->next;tail->next = NULL;while (p) {LNode* next = p->next;q = L->next;while (q) {if (fabs(q->data) == fabs(p->data)) {free(p);p = next;break;}else {q = q->next;}}if (q == NULL) {tail->next = p;tail = p;tail->next = NULL;p = next;}}
}

29.线性表(a1,a2,a3, ...an)中元素递增有序且按顺序存储于计算机内。要求设计算法完成:
(1) 用最少时间在表中查找数值为x的元素。
(2)若找到,将其与后继元素位置相交换。
(3)若找不到,将其插入表中并使表中元素仍递增有序。

void findXandSwapFromLinkedList(LinkedList& L, ElemType x) {LNode* p = L;LNode* pre = NULL;while (p && p->data < x) {pre = p;p = p->next;}// 找到了元素值为x的节点if (p && p->data == x) {// 如果该节点不是最后一个节点if (p->next) {// 交换当前节点与后继节点的位置ElemType temp = p->data;p->data = p->next->data;p->next->data = temp;}}// 没找到元素值为x的节点,将其插入有序链表中else {LNode* node = (LNode*)malloc(sizeof(LNode));node->data = x;// 如果pre为NULL,说明x应该插入到链表头部if (!pre) {node->next = L;L = node; // 更新链表头指针}else {node->next = pre->next;pre->next = node;}}
}

30.已知三个带头结点的线性链表A、B和C中的结点均依元素值自小至大非递减排列(可能
存在两个以上值相同的结点),编写算法对A表进行如下操作:使操作后的链表A中仅留下.
三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。限定算
法的时间复杂度为O(m+n+p),其中m. n和p分别为三个表的长度。[清华大学(15
分)]

void removeDuplicates(LinkedList& A, LinkedList B, LinkedList C) {LNode* pa = A->next;LNode* pb = B->next;LNode* pc = C->next;LNode* tail = A;tail->next = NULL;while (pa && pb && pc) {if (pa->data < pb->data || pa->data < pc->data) {LNode* removed = pa;pa = pa->next;free(removed);}else if (pa->data > pb->data || pa->data > pc->data) {// A链表当前节点的值大于B或C链表当前节点的值,则分别移动B和C链表的指针if (pb->data < pc->data) {pb = pb->next;}else if (pb->data > pc->data) {pc = pc->next;}else {pb = pb->next;pc = pc->next;}}else {tail->next = pa;tail = pa;pa = pa->next;pb = pb->next;pc = pc->next;tail->next = NULL;}}while (pa) {LNode* removed = pa;pa = pa->next;free(removed);}
}

1.3循环单链表

typedef int ElemType;typedef struct LNode {ElemType data;LNode* next;
}LNode;
typedef struct LinkedList {LNode* head;	//头结点LNode* tail;	//尾结点int length;		//链表长度
}LinkedList;//初始化单链表
void initLinkedList(LinkedList& L) {L.head = NULL;	//初始化头指针尾指针为空长度为0L.tail = NULL;L.length = 0;
}
//头插法创建单链表
void createLinkedListWithHead(LinkedList& L) {ElemType x;scanf("%d", &x);while (x != 999) {LNode* node = (LNode*)malloc(sizeof(LNode));node->data = x;if (L.head == NULL) {	//如果是创建的第一个元素,头指针尾指针都指向当前元素L.head = node;L.tail = node;}else {node->next = L.head;	//如果不是第一个元素和普通头插法一样L.head = node;}L.tail->next = L.head;		//每次要把尾指针的next指向头指针L.length++;					//维护表长正确性scanf("%d", &x);}
}
//尾插法创建单链表
void createLinkedListWithTail(LinkedList& L) {ElemType x;scanf("%d", &x);while (x != 999) {LNode* node = (LNode*)malloc(sizeof(LNode));node->data = x;	if (L.head == NULL) {		//如果是创建的第一个结点L.head = node;L.tail = node;}else {L.tail->next = node;	//尾指针的next指向新创建的结点L.tail = node;			//尾指针来到新结点处}L.tail->next = L.head;		//每次尾指针的next指向头指针L.length++;scanf("%d", &x);}
}
//返回第pos个位置上的结点
LNode* getNode(LinkedList L, int pos) {//位置不对或表位空返回NULLif (pos < 0 || L.head == NULL) {return NULL;}if (pos == 1) {		//如果要找第一个位置结点元素直接返回return L.head;}int i = 1;LNode* p = L.head->next;	//从第二个结点开始查找while (p != L.head && i < pos - 1) {p = p->next;i++;}return p == L.head ? NULL : p;	//如果再一次来到头指针即第一个元素位置证明没找到返回NULL,否则返回找到的p
}
//向表尾插入元素e
void insertElemToTail(LinkedList& L, ElemType e) {LNode* node = (LNode*)malloc(sizeof(LNode));node->data = e;if (L.head == NULL) {	//如果链表为空,创建新结点让头尾指针都指向新结点L.head = node;L.tail = node;}else {				L.tail->next = node;L.tail = node;}L.tail->next = L.head;L.length++;
}
//打印链表
void printLinkedList(LinkedList L) {LNode* p = L.head;//分开打印是因为刚开始p的位置即为L.head,当p第二次来到L.head时说明已经遍历一轮了,为了避免下面while刚进去就退出条件的情况,如果是带头结点的循环单链表不需要单独分开打印if (p) {printf("%d->", p->data);p = p->next;}while (p != L.head) {printf("%d->", p->data);p = p->next;}printf("\n");
}

1.4双链表

typedef int ElemType;
typedef struct DNode {ElemType data;	//数据域DNode* prior;	//前驱指针域DNode* next;	//后继指针域
}DNode,*DLinkedList;//双链表的初始化(带头结点)
void initDLinkedList(DLinkedList& L){L = (DNode*)malloc(sizeof(DNode));if (L) {L->next = NULL;L->prior = NULL;L->data = 0;}
}
//头插法创建单链表
void createDListWithHead(DLinkedList& L){ElemType x;scanf("%d", &x);while (x!=999){DNode* node = (DNode*)malloc(sizeof(DNode));if (node) {node->data = x;node->next = L->next;if (L->next != NULL) {L->next->prior = node;}L->next = node;node->prior = L;L->data++;}scanf("%d", &x);}
}//尾插法创建单链表
void createDListWithTail(DLinkedList& L) {ElemType x;scanf("%d", &x);DNode* tail = L;while (x != 999) {DNode* node = (DNode*)malloc(sizeof(DNode));if (node) {node->data = x;node->next = NULL;tail->next = node;node->prior = tail;tail = node;L->data++;}scanf("%d", &x);}
}//返回第pos个位置上的结点
DNode* getNode(DLinkedList L, int pos) {//检查pos是否合法,data是链表的长度if (pos<0 || pos>L->data) {return NULL;}int i = 0;DNode* p = L;while (p && i < pos) {p = p->next;i++;}return p;
}
//在第pos个位置插入元素e
void insertElem(DLinkedList& L, int pos, ElemType e) {if (pos<1 || pos>L->data + 1) {return;}DNode* node = (DNode*)malloc(sizeof(DNode));if (node) {node->data = e;DNode* pre = getNode(L, pos - 1);node->next = pre->next;if (pos != L->data + 1) {pre->next->prior = node;}pre->next = node;node->prior = pre;L->data++;}}
//删除第pos个位置上的元素。并用e接收被删除元素值
void deleteElem(DLinkedList& L, int pos, ElemType& e) {if (pos<1 || pos>L->data) {return;}DNode* pre = getNode(L, pos - 1);DNode* removed = getNode(L, pos);if (pos == L->data) {pre->next = NULL;}else {pre->next = removed->next;removed->next->prior = pre;}e = removed->data;free(removed);L->data--;
}
//打印双链表
void printDList(DLinkedList L) {DNode* p = L->next;if (p == NULL) {return;}printf("%d->", p->data);printDList(p);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/773185.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c 语言中 用 %d输出double型数据;%f输出int型数据

001、 %f输出int型数据,其值为0.[root@PC1 test]# ls test.c [root@PC1 test]# cat test.c ## 测试程序 #include <stdio.h>int main(void) {int i = 10;printf("i = %f\n", i); // %f输出int型数据return 0; } [root@PC1 test]# gcc te…

一觉醒来,虚拟机的IP变了【解决】

为了模拟服务器,我使用了虚拟机来连接我的后端程序。配置了很多信息,就比如我的虚拟机ip。但是一觉醒,来再次启动发现虚拟机的ip变了!!!于是,我查阅了相关资料......解决办法 1修改配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33TYPE=Ethernet PROXY_METHOD=…

408 数据结构队列算法

第三章 队列 3.1顺序队列 #define MAXSIZE 64 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int front; //队头指针int rear; //队尾指针int size; //队列大小 }SeQueue;//初始化队列 void initQueue(SeQueue& Q) {//对数据元素进行初始化,防止出现脏数…

408数据结构树算法

第四章 树 4.1 二叉树的顺序存储 #define MAXSIZE 16 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int size; }Tree;//初始化二叉树 void initTree(Tree& T) {for (int i = 0; i < MAXSIZE; i++) {T.data[i] = 0; //假设0表示空节点}T.size = 0; } /…

多项式全家桶

多项式合集 前置知识:多项式的定义,表示方法,FFT,NTT,微积分等。 注意事项多项式的封装很重要,现在一般都是用将指针传入一个函数的方式来进行多项式操作,如:void Inv(ll *f,ll *g,int n),表示对 \(n\) 次多项式 \(f\) 求逆,结果存在 \(g\) 中。 多项式数组多了一定要…

KD-Tree 学习笔记

好想睡觉KD-Tree 学习笔记 建树如果当前超长方体只有一个点,返回这个点 选择一个维度(轮流) 选择中位数(\(O(n)\)) 递归应用定理二维 KDT 中节点代表矩阵与任意一个矩形(边界上)有交的只有 \(O(\sqrt n)\) 个。证明: 考虑一条直线,与KDT的交集,此层最多有两个,递归得…

jmeter 参数理解

线程数 线程数用于模拟并发用户数,每个线程代表一个虚拟用户;线程组内可以有1-多个接口请求,同属于一个线程,理解为单个用户的事务操作;如果只有一个接口那就说单接口的性能 Ramp-Up时间(秒) 定义了所有线程启动所需的时间,合理设置ramp-up时间可以避免瞬时加压,使性能测…

Linux系统中的Vim编辑器

本章将和大家分享Linux系统中的Vim编辑器。本章将和大家分享Linux系统中的Vim编辑器。废话不多说,下面我们直接进入主题。 一、Vim 入门帮助 Vim 带有完整的帮助文档:进入 Vim 后输入“:help”即可访问 二、Vim 模式介绍 与大部分其它编辑器不同,进入 Vim 后,缺省状态下键入…

2024暑假集训测试14

前言比赛链接。最可惜的一点还是本来 T3 暴力能拿 \(20\),优化成 \(15\) 了,不然就 rk2 了,晚上可能又有泡面吃了。 不过因为 T2、T4 两道水题,剩下两道不太可做(至少对于我是这样的),这两题不挂分的打的貌似都不错。 T3 没学过莫反输麻了。 T1 黑暗型高松灯 本来应该是…

一篇文章教你如何读懂 JMeter聚合报告参数!

在进行性能测试时,JMeter是一款备受推崇的开源工具。而其中的聚合报告(Aggregate Report)是我们分析测试结果、了解系统性能的重要依据。今天,我们就来深入探讨如何读懂JMeter聚合报告中的各项参数。 面对复杂的聚合报告,究竟哪些参数是我们必须关注的?这些参数背后又隐藏…

[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂

标题即摘要,暂不赘述。 如果文中有不妥或不对的,多多交流。【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18328759 出自【进步*于辰的博客】参考笔记一,P10.4、P13.2;笔记三,P48.1。目录先言1、通用函数2、…

Python 代码中的 yield 到底是什么?

在Python编程中,有一个强大而神秘的关键字,那就是yield。初学者常常被它搞得晕头转向,而高级开发者则借助它实现高效的代码。到底yield是什么?它又是如何在Python代码中发挥作用的呢?让我们一起来揭开它的面纱。 Python里的一个非常重要但也颇具迷惑性的关键词——yield。…