顺序表
静态分配内存及初始化
c++
#include<stdio.h>
#include<stdlib.h>
#define Maxsize 100
//静态
typedef struct sqlist {
int data[Maxsize];
int length;
}SqList;
void InitList(SqList& L) {
L.length = 0; //顺序表的初始长度为0
}
动态分配内存及初始化
c++
//动态
typedef struct {
int* data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SeqList;
void InitList(SeqList& L) {
L.data = (int*)malloc(Maxsize * sizeof(int));
L.length = 0;
L.MaxSize = Maxsize;
}
01 对顺序表L进行遍历并输出每个数据元素的数据值
c++
//对顺序表L进行遍历并输出每个数据元素的数据值
void print(SqList &L){
for(int i=0;i<L.length;i++){
printf("%d",L.data[i]);
}
}
02 假设有一个顺序表L,其存储的所有数据元素均为不重复的正数,查找 L 中值为 e 的数据元素,若找到则返回其下标,若找不到则返回-1。
c++
int search_e(SqList L, int e) {
for (int i = 0; i < L.length; i++) {
if (e == L.data[i]) {
return i;
}
}
return -1;
}
03 假设有一个顺序表 L,其存储的所有数据元素均为正数,查找 L 中第 i 个数据元素并返回其值。
c++
//假设有一个顺序表 L,其存储的所有数据元素均为正数,查找 L 中第 i 个数据元素并返回其值。
int Get_i(SqList L, int i) {
if (i >= 1 && i <= L.length) {
return L.data[i - 1];
}
return -1;//越界
}
①注意越界的判断
04 设计一个高效算法,将顺序表 L 的所有元素逆置,要求算法的空间复杂度为 O(1) 。
- 逆置:实现头和尾的转换
- 空间复杂度O(1):不能去定义数组啥的,只能用常量级别解决。
- 4个元素,交换两次,5个元素也交换两次,交换次数规律是length/2向下取整,而计算机中自动向下取整,这里大家可以自己去试着模拟一下,i < L.length/2,如果取等号,偶数个元素的顺序表,中间的两个元素会重新换回来(i多走了一步)。
- 逆置时用下标找规律。
c++
//设计一个高效算法,将顺序表 L 的所有元素逆置,要求算法的空间复杂度为 O(1)
void Reverse(SqList& L) {
int temp;
for (int i = 0; i < L.length/2; i++) {
temp = L.data[i];
L.data[i] = L.data[L.length - i - 1];
L.data[L.length - i - 1] = temp;
}
}
05 在顺序表 L 的第 i 个位置插入新元素 e。若 i 的输入不合法,则返回 false, 表示插入失败;否则,将第 i 个元素及其后的所有元依次往后移动一个位置, 腾出一个空位置插入新元素 e,顺序表长度增加 1,插入成功,返回 true。
- 注意插入的位置可以是1~length+1;
- 顺序表存满了也不能存了
- 从后往前的移动元素
- 判断条件
c++
//在顺序表 L 的第 i 个位置插入新元素 e。若 i 的输入不合法,则返回 false,表示插入失败;否则,将第 i 个元素及
//其后的所有元素依次往后移动一个位置,腾出一个空位置插入新元素 e,顺序表长度增加 1,插入成功,返回 true。
bool Insert(SqList &L, int i, int e) {
if (i<1 || i>L.length+1) {
return false;
}
if (L.length == Maxsize) {
return false;
}
for (int j = L.length; j > i-1; j--) {
L.data[j] = L.data[j - 1];
}
L.data[i - 1] = e;
L.length++;
return true;
}
06 已知一个顺序表 L,其中的元素递增有序排列,设计一个算法在插入元素 x(x 为 int 型)后保持该顺序表仍然递增有序排列,假设c插入操作肯定成功,插入成功后返回插入元素所在位置。
分析
-
先找出位置
-
插入元素
-
注意:要返回插入元素位置,i的定义要放在for循环外
-
举例(从后往前移动元素,第一行为顺序表,第二行为下标)
①假设x=5,即i=3,初始j=4,判断移动一次,故判断条件为j>i或者j>=i+1
②假设x=3,即i=2,初始i=4,判断移动两次,第一次移动条件j=4,i=2,第二次移动j=3,i=2,故判断条件为j>i或者j>=i+1
综上,判断条件为j>i或者j>=i+1
{c++
int Insert(SqList &L,int x){
int i;//定义变量 i 记录元素 x 插入位置
for(i=0; i<L.length; i++){
if(L.data[i]>x){
break;
}
}
//也可以换成for(i=0; i<L.length,L.data[i]<=x; i++);
//自己模拟j的条件(易错)
for(j=L.length;j>i;j--){
L.data[j]=L.data[j-1];
}
L.data[i]=x;
L.length++;
return i;
}
07 删除顺序表 L 中第 i 个位置的元素,若 i 的输入不合法,则返回 false; 否则将被删元素赋给引用变量 e,并将第 i+1 个元素及其后的所有元素依次往前 移动一个位置,返回 true。
分析
-
注意i是位置,和下标不一样
-
被删元素赋给引用变量 e
-
举例
位置 1 2 3 4 5
元素 2 5 1 3 4
下标 0 1 2 3 4
删除第2个位置,i=2,L.length=4,下标为1,要移动两次(j=2,j=3),判断条件为j<L.length
-
最后别忘了顺序表长度改变
c++
bool ListDelete(SqList &L, int i, int &e){
if(i<1||i>L.length){
return false;
}
e=L.data[i-1];
for(int j=i; j<L.length; j++){
//后面赋值给前面
L.data[j-1]=L.data[j];
}
L.length--;
return ture;
}
08 从顺序表 L 中删除最小值元素并由函数返回被删元素的值。(假设顺序表中数据元素全为正值且最小值唯一)
分析
- 先找到最小值,在进行删除
- 要判断不合理情况,如空的顺序表
- 首先定义最小值为下标为0的元素,记录其位置pos
由于最后需要返回被删元素的值,因此需要有个变量对最小值进行保存,然后删除操作过程中,最小值被删除,此后如果没有保存最小元素的变量,则找不到最小值。
c++
int Delete_min(SqList &L)[
if(L.length==0){
return 0;
}
//查找最小值
int min=L.data[0],pos=0;
for(int i=1;i<L.length;i++){
if(L.data[i]<min){
min=L.data[i];
pos=i;
}
}
//删除操作,首先定义
//j<L.length-1可以自己模拟出,假设长度为五,假设最小值时,i=pos=3,另j=i,说明只需要移动一次即可,判断条件
for(int j=pos; j<L.length-1;j--){
L.data[j]=L.data[j+1];
}
//减少长度
L.length--;
return min;
]
09 对长度为 n 的顺序表 L,编写一个时间复杂度为 O(n)、空间复杂度为 O(1) 的算法,该算法删除顺序表中所有值为 x 的数据元素。
- x可能不止一个,因此后面元素前移的数量需要改变
- 定义一个变量k用来记录目前已经删除几个元素,k=1表示后面的元素需要前移1个
- 元素要保留,需要交换,元素不保留,不需要交换,先本次交换,然后在k再加1.
法一:
c++
void Delete_all_x(SqList &L,int x){
int k=0;
for(int i=0;i<L.length;i++){
if(L.data[i]==x){
//应该删除
k++;
}
else{
//重点
L.data[i-k]=L.data[i];
}
}
L.length=L.length-k;
}
法二:
思路:将顺序表需要保存的元素理解为排队,直接用一个变量记录排队位置,
c++
void Delete_all_x(SqList &L,int x){
int k=0;
for(int i=0; i<L.length; i++){
if(L.data[i]!=x){
//需要保留
L.data[k]=L.data[i];
k++;
}
}
//不是k+1,因为排队结束会执行k++的操作
L.length = k;
}
改成while循环也类似:
cpp
void Delete_all_x(SqList &L,int x){
int k=0;
int i=0;
while(i<L.length){
if(L.data[i]!=x){
k++;
}
i++;
}
//不是k+1,因为排队结束会执行k++的操作
L.length = k;
}
10 从顺序表中删除其值在给定值 s 与 t 之间(包含 s 和 t,要求 s<t)的所有元素,若 s 或 t 不合理或顺序表为空,则返回 false,若执行成功则返回 true。
- 和上一题一样,有两种实现方式
法一:
cpp
bool Del(SqList &L,int s,int t){
if(s>=t||L.length==0) //若 s 或 t 输入不合法或顺序表为空,则返回 false
return false;
int k=0; //k 用来记录要删除元素的数量,初始时为 0
for(int i=0;i<L.length;i++){ //遍历顺序表 L
if(L.data[i]>=s&& L.data[i]<=t) //若元素符合删除条件则 k 加一
k++;
}
else{ //若元素需保留则前移 k 个位置
L.data[i-k]= L.data[i];
}
L.length= L.length-k; //更新顺序表长度
return true; //执行结束返回 true
}
法二
cpp
bool Del(SqList &L,int s,int t){
if(s>=t||L.length==0) //若 s 或 t 输入不合法或顺序表为空,则返回 false
return false;
int j=0; //j 用来记录保留元素的排队位置
for(int i=0;i<L.length;i++){ //遍历顺序表 L
if(L.data[i]<s||L.data[i]>t){ //判断此次遍历到的元素是否为保留元素
L.data[j]=L.data[i]; //是保留元素则过去排队
j++; //新元素排队后需更新排队位置
}
}
L.length= j; //更新顺序表长度
return true; //执行结束返回 true
}
11 从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
- 本题目是有序顺序表,这个算法和前面两个有所区别,在于从第二个元素开始,每次和前一个元素比较;
- 之前是从第一个元素开始,所以记录排队位置是0,这次是第二个元素开始(第一个元素肯定不重复,不需要交换),因此我们可以记录排队位置变量初始设置为1,然后根据这个进行后续代码的改动,还是那句话元素要保留,需要交换,最后注意一下顺序表长度即可。
cpp
void Del_Same(SqList &L){
int j=1; // j 用来记录保留元素的排队位置
for(int i=1;i<L.length;i++){ //遍历顺序表 L
if(L.data[i]!=L.data[j-1]){ //判断此次遍历到的元素是否为保留元素
L.data[j]=L.data[i]; //是保留元素则过去排队
j++; //新元素排队后需更新排队位置
}
}
L.length=j; //更新顺序表长度
}
12 从有序顺序表中删除其值在给定值 s 与 t 之间(包含 s 和 t,要求 s<t)的所有元素,若 s 或 t 不合理或顺序表为空,则返回 false,若执行成功则返回 true。
- 这题目的思路是定义两个变量分别去待删元素的起始位置和结束位置,这里需要注意的是左边是小于s,这样最后一次j就来到了待删元素处,右边是小于等于,这样的话最后一次i来到了待删元素的后一个位置。
cpp
bool Del_s_t(SqList &L,int s,int t){
if(s>=t||L.length==0){ //若 s 或 t 输入不合法或顺序表为空则返回 false
return false;
}
int i,j; //定义两个变量 i 和 j 负责记录待删元素的起始位置和结束位置
for(j=0;j<L.length&&L.data[j]<s;j++); //寻找待删元素起始位置
if(j==L.length){ //若顺序表中所有元素都比 s 小则没有要删元素
return false;
}
for(i=j;i<L.length&&L.data[i]<=t;i++); //寻找待删元素的结束位置
for(;i<L.length;i++,j++){ //将结束位置后的保留元素前移至起始位置
L.data[j]=L.data[i];
}
L.length=j; //更新顺序表长度
return true; //执行结束则返回 true
}
13 将两个有序顺序表 A 和 B 合并为一个新的有序顺序表 C,若合并成功则返回 true,合并失败则返回 false。
- 首先判断一下是否有足够空间进行合并,然后需要三个遍历指针进行遍历,最后记得更新C表表长
cpp
bool Merge(SeqList A, SeqList B, SeqList& C) {
if (A.length + B.length > C.Maxsize) //判断 C 存储空间是否能容纳 A 和 B
return false;
int i = 0, j = 0, k = 0; //定义三个遍历指针,分别遍历 ABC
while (i < A.length && j < B.length) { //只有 A 和 B 都没遍历完才可以继续循环
if (A.data[i] <= B.data[j]) { //若 A 表中值更小则 A 表中遍历元素赋值给 C 表
C.data[k] = A.data[i];
k++; //更新 C 表元素插入位置
i++; //更新 A 表遍历位置
}
else { //若 B 表中值更小则 B 表中遍历元素赋值给 C 表
C.data[k] = B.data[j];
k++; //更新 C 表元素插入位置
j++; //更新 B 表遍历位置
}
}
while (i < A.length) { //若循环结束后 A 还有未遍历元素则顺序赋值给 C
C.data[k] = A.data[i];
k++;
i++;
}
while (j < B.length) { //若循环结束后 B 还有未遍历元素则顺序赋值给 C
C.data[k] = B.data[j];
k++;
j++;
}
C.length = k; //更新 C 表长度
return true; //执行结束返回 true
}