目录
一、顺序表的常用操作
顺序表的常用操作有建表、插入、删除、查找、打印等等。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
typedef int ElementType;
const int MAXSIZE = 50; //最多存放5个元素
struct sqList
{
ElementType data[MAXSIZE];
int length;
};
/**
* 初始化
*/
sqList MakeEmpty()
{
sqList L;
L.length = 0;
return L;
}
/**
* 插入
* 本算法实现将元素x插入到顺序表L中第i个位置
*/
bool Insert(sqList &L, ElementType x, int i)
{
if (L.length == MAXSIZE)
{
printf("表满\n");
return false;
}
if (i < 1 || i > L.length + 1)
{
printf("位序不合法\n");
return false;
}
for (int j = L.length; j >= i; j++)
{
L.data[j] = L.data[j - 1];
}
L.data[i - 1] = x;
L.length++;
return true;
}
/**
* 顺序打印表
*/
void print(sqList L)
{
if (L.length == 0)
{
printf("表空\n");
return;
}
for (int i = 0; i < L.length; i++)
{
printf("第%d个位置:%d\n", i, L.data[i]);
}
}
int main()
{
sqList L = MakeEmpty();
return 0;
}
二、习题
①选择题
![]() | A 顺序表不像链表那样要在结点中存放指针域,因此存储密度较大,A正确。B和C是链表的优点。D是错误的,比如对于树形结构,顺序表显然不如链表表示起来方便。 |
![]() | A 本题容易误选B.顺序表是一种支持随机存取的顺序存储结构,根据起始地址加上元素的序号,可以很方便地访问任意一个元素,即随机存取的概念。注意,顺序存取是一种读写方式,不是存储方式,有别于顺序存储。 |
![]() | D 题干实际要求能够最快存取第i-1、i和i+1个元素值。A、B、C都只能从头结点依次顺序查找,时间复杂度为O(n),只有顺序表可以随机存取,时间复杂度为O(1)。 |
![]() | D 表元素序号从1开始,而在第n+1个位置插入相当于在表尾追加。 |
②解答题
/**
* 天道-数据结构-P30-第1题:
* 从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删元素的值。
* 空出的位置由最后一个元素填补,若顺序表为空则显示出错信息并退出运行。
*/
bool Del_Min(sqList &L, ElementType &value)
{
if (L.length == 0)
{
return false;
}
value = L.data[0];
int pos = 0;
for (int i = 1; i < L.length; i++)
{
if (value < L.data[0])
{
value = L.data[0];
pos = i;
}
}
L.data[pos] = L.data[L.length - 1];
L.length--;
return true;
}
/**
* 天道-数据结构-P30-第2题:
* 设计一个高效算法,将顺序表工的所有元素逆置,要求算法的空间复杂度为O(1)。
*/
void Reverse(sqList &L)
{
for (int i = 0, j = L.length - 1; i <= j; i++, j--)
{
ElementType temp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = temp;
}
/*or
for (int i = 0; i < L.length / 2; i++)
{
ElementType temp = L.data[i];
L.data[i] = L.data[L.length - i - 1];
L.data[L.length - i - 1] = temp;
}
*/
}
/**
* 天道-数据结构-P30-第3题:
* 对长度为n的顺序表L,编写一个时间复杂度为O(n)、空间复杂度为O(1)的算法,
* 该算法删除线性表中所有值为x的数据元素
*/
void Del_All_x(sqList &L, ElementType x)
{
/* 解法一:用k记录顺序表L中不等于x的元素个数(即需要保存的元素个数),
边扫描L边统计k,并将不等于x的元素向前移动k个位置,最后修改L的长度.*/
int k = 0;
for (int i = 0; i < L.length; i++)
{
if (L.data[i] != x) //记录值不等于x的元素个数
{
L.data[k] = L.data[i];
k++; //不等于x的元素增1
}
}
L.length = k; //顺序表L的长度等于k
/*解法二:用k记录顺序表L中等于x的元素个数,边扫描L边统计k,
并将不等于x的元素前移k个位置,最后修改L的长度。*/
/*int k = 0;
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == x) //记录值不等于x的元素个数
k++; //不等于x的元素增1
else
L.data[i - k] = L.data[i];
}
L.length -= k; //顺序表L的长度等于k */
}
/**
* 天道-数据结构-P30-第4题:
* 从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,
* 如果s或t不合理或顺序表为空,则显示出错信息并退出运行。
* 注意点:
* 可以用第3题的方法,但区别于第3题,这道题的链表是有序的,所以删除的元素必然是相连的整体。
* 算法思想:
* 先寻找值大于等于s的第一个元素(第一个删除的元素),然后寻找值大于t的第一个元素
* (最后一个删除的元素的下一个元素),要将这段元素删除,只需直接将后面的元素前移。
*/
bool DEL_s2t(sqList &L, ElementType s, ElementType t)
{
if (s >= t)
{
printf("区间不合法\n");
return false;
}
int i, j;
for (i = 0; L.data[i] < s && i < L.length; i++) //寻找值大于等于s的第一个元素
;
if (i == L.length)
{
printf("所有元素都小于s,无法进行删除操作");
return false;
}
for (j = i; L.data[j] <= t && j < L.length; j++) //寻找值大于t的第一个元素
;
for (; j < L.length; i++, j++) //前移,填补被删元素位置
{
L.data[i] = L.data[j];
}
L.length = i;
return true;
}
/**
* 天道-数据结构-P30-第5题:
* 从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,
* 如果s或t不合理或顺序表为空,则显示出错信息并退出运行。
* 算法思想:
* 可以完全借用第三题的思想。
*/
bool DEL_s2t2(sqList &L, ElementType s, ElementType t)
{
if (s >= t)
{
printf("区间不合法\n");
return false;
}
int k = 0;
for (int i = 0; i < L.length; i++)
{
if (L.data[i] < s || L.data[i] > t)
{
L.data[k] = L.data[i];
k++;
}
}
L.length = k;
/*解法二:
int k = 0;
for (int i = 0; i < L.length; i++)
{
if (L.data[i] >= s && L.data[i] <= t)
k++;
else
L.data[i - k] = L.data[i];
}
L.length -= k;
*/
return true;
}
/**
* 天道-数据结构-P30-第7题:
* 从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
* 算法思想:
* 因为是顺序表,所以重复的元素都是连续出现的。
* 遍历顺序表,用k记录顺序表L中第一次出现的元素的数量,边扫描L边统计k,
* 并将第一次出现的元素向前移动k个位置,重复出现的元素跳过,不做统计,
* 最后修改L的长度。
* 算法思想2:
* 注意是有序顺序表,值相同的元素一定在连续的位置上,用类似于直接插入排序的思想,
* 初始时将第一个元素视为非重复的有序表。之后依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,
* 若相同则继续向后判断,若不同则插入到前面的非重复有序表的最后,直至判断到表尾为止。
* 两种思路基本相同,
*/
bool DEL_Same(sqList &L)
{
if (L.length == 0)
return false;
int k = 1; //第一个位置元素肯定是首次出现的,
for (int i = 1; i < L.length; i++) //从位置1开始遍历,不然i-1 = -1出现负数下标,会运行错误
{
if (L.data[i] != L.data[i - 1])
{
L.data[k] = L.data[i];
k++;
}
}
L.length = k;
/*or
int i, j;
for (i = 0, j = 1; j < L.length; j++) //i存储第一个不相同的元素,j为工作指针
{
if (L.data[i] != L.data[j]) //查找下一个与上个元素值不同的元素
L.data[++i] = L.data[j]; //找到后,将元素前移
}
L.length = i + 1;
*/
return true;
}
/**
* 天道-数据结构-P30-第7题:
* 将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表.
* 算法思想:
* 首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。
* 然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。
*/
bool Merge(sqList A, sqList B, sqList &L) //将有序顺序表A与B合并为一个新的有序顺序表L
{
if (A.length + B.length > MAXSIZE) //大于顺序表的最大长度
return false;
int i = 0, j = 0, k = 0;
while (i < A.length && j < B.length) //循环,两两比较,小者存入结果表
{
if (A.data[i] < B.data[j])
L.data[k++] = A.data[i++];
else
L.data[k++] = B.data[j++];
}
while (i < A.length) //还剩一个没有比较完的顺序表
{
L.data[k++] = A.data[i++];
}
while (j < B.length)
{
L.data[k++] = B.data[j++];
}
L.length = k;
return true;
}
/**
* 天道-数据结构-P30-第8题:
* 已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3,… ,am)和(b1,b2,b3,… ,bn).
* 试编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,…,bn)放在(a1,a2,a3,…,am)的前面.
* 算法思想:
* 首先将数组A[m+n]中的全部元素(a1,a2,a3…,am,b1,b2,b3,…,bn),
* 原地逆置为(bn,bn-1,bn-2,…,b1,am,am-1,am-2,…,a1),
* 再对前n个元素和后m个元素分别使用逆置算法,
* 即可得到(b1,b2,b3,…,bn,a1,a2,a3,…,am),从而实现顺序表的位置互换。
*/
void Reverse2(ElementType A[], int left, int right)
{
int mid = (left + right) >> 1;
for (int i = 0; i <= mid - left; i++)
{
int temp = A[left + i];
A[left + i] = A[right - i];
A[right - i] = temp;
}
}
void Exchange(ElementType A[], int m, int n)
{
Reverse2(A, 0, m + n - 1);
Reverse2(A, 0, n - 1);
Reverse2(A, n, m + n - 1);
}
/**
* 天道-数据结构-P30-第9题:
* 线性表(a1,a2,a3,…,an)中的元素递增有序且按顺序存储于计算机内。
* 要求设计一算法,完成用最少时间在表中查找数值为x的元素,
* 若找到则将其与后继元素位置相交换,若找不到则将其插入表中并使表中元素仍递增有序。
* 算法思想:
* 顺序存储的线性表递增有序,可以顺序查找,也可以折半查找。
* 题目要求“用最少的时间在表中查找数值为x的元素”,这里应使用折半查找法。
*/
void Find_Exchange_Insert(ElementType A[], int n, int x)
{
int left = 0, right = n, mid;
while (left <= right)
{
mid = (left + right) >> 1;
if (x < A[mid])
right = mid - 1;
else if (x > A[mid])
left = mid + 1;
else
break;
}
if (A[mid] == x && mid != n - 1)
{
ElementType temp = A[mid];
A[mid] = A[mid + 1];
A[mid + 1] = temp;
}
if (left > right)
{
for (int i = n - 1; i > right; i--)
A[i + 1] = A[i];
A[right + 1] = x;
}
}
/**
* 【2010统考真题】
* 设将n(n>1)个整数存放到一维数组R中。设计一个在时间和空间两方面都尽可能高效的算法。
* 将R中保存的序列循环左移p(0<p<n)个位置,
* 即将R中的数据由(X0,X1,…,Xn-1)变换为(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)要求:
* 1)给出算法的基本设计思想。
* 2)根据设计思想,采用C或C艹或Java语言描述算法,关键之处给出注释
* 3)说明你所设计算法的时间复杂度和空间复杂度。
* 算法设计思想:
* 令(X0,X1,…,Xp-1)为a,(Xp,Xp+1,…,Xn-1)为b。
* 原数组R就可以表示为ab,题目要求的结果可以看做ba。
* 现在分别将a,b倒转,得到~a~b为(Xp-1,…,X1,X0, Xn-1,…,Xp+1,Xp)。
* 然后将~a~b整个倒转,得到~(~a~b)=ba, 即(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)。
*
* 设Reverse函数执行将数组元素倒转的操作,对abc defgh向左循环移动3(p=3)个位置的过程如下
* Reverse(0,p-1)得到 cba defgh;
* Reverse(p,n-1)得到 cba hgfed
* Reverse(0,n-1)得到 defgh abc;
*
*/
void Reverse3(int R[], int left, int right)
{
int mid = (left + right) >> 1;
for (int i = 0; i <= mid - left; i++)
{
int temp = R[left + i];
R[left + i] = R[right - i];
R[right - i] = temp;
}
}
void leftShift_p(int R[], int n, int p)
{
Reverse3(R, 0, p - 1);
Reverse3(R, p, n - 1);
Reverse3(R, 0, n - 1);
}
/**
* 【2011统考真题】
* 一个长度为L(L>1)的升序序列S,处在第L2个位置的数称为S的中位数。
* 例如,若序列S1=(11,13,15,17,19),则S1的中位数是15,
* 两个序列的中位数是含它们所有元素的升序序列的中位数。
* 例如,若S2=(2,4,6,8,20),则S1和S2的中位数是11。
* 现在有两个等长升序序列A和B,
* 试设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列A和B的中位数。
* 要求:
* 1)给出算法的基本设计思想。
* 2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释
* 3)说明你所设计算法的时间复杂度和空间复杂度。
* 算法思想:
* 对两个序列认为是一个升序序列S3,则S3[L]就是两个序列的中位数。(找出第L个元素即可,不用合并)
* 复杂度:
* 时间复杂度O(n) ,空间复杂度O(1)
*/
void Find_Mid(ElementType A[], ElementType B[], int L, int &mid)
{
int i = 0, j = 0;
while (i + j < L)
{
if (A[i] < B[j])
mid = A[i++];
else if (A[i] > B[j])
mid = B[j++];
}
}
/**
* 【2018统考真题】
* 给定一个含n(n≥1)个整数的数组,请设计一个在时间上尽可能高效的算法,
* 找出数组中未出现的最小正整数。
* 例如,数组{-5,3,2,3}中未出现的最小正整数是1;
* 数组{1,2,3}中未出现的最小正整数是4.要求:
* 1)给出算法的基本设计思想
* 2〕根据设计思想,采用C或C艹语言描述算法,关键之处给出注释。
* 3)说明你所设计算法的时间复杂度和空间复杂度。
*/
void findMissMin(int A[], int n, int &min)
{
int *B;
B = (int *)malloc(sizeof(int) * n);
memset(B, 0, sizeof(B));
for (int i = 0; i < n; i++)
if (A[i] > 0)
B[A[i] - 1] = 1;
for (int i = 0; i < n; i++)
{
if (B[i] = 0)
{
min = B[i];
break;
}
}
}