文章目录
所有知识点及题目均来自王道数据结构2019版!
定义
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int length;
}SqList;
基本操作
插入
bool ListInsert(SqList &L, int i, ElemType e){
//将元素e插入到顺序表L中的第i个位置
if(i < 1 || i > L.length+1)
return false;
if(L.length >= MaxSize)
return false;
for(int j = L.length; j >= i; j--)
L.data[j] = L.data[j-1];
L.data[i-1] = e;
L.length++;
return true;
}
删除
bool ListDelete(SqList &L, int i, ElemType &e){
//删除顺序表L中第i个位置的元素
if(i < 1 || i > L.length)
reutrn false;
e = L.data[i-1];
for(int j = i; j < L.length; j++)
L.data[j-1] = L.data[j];
L.length--;
return true;
}
按值查找
int LocateElem(SqList L, ElemType e){
//查找顺序表中值为e的元素,如果查找成功,返回元素位序,否则返回0
int i;
for(i = 0; i < L.length; i++)
if(L.data[i] == e)
return i+1;
reutrn 0;
}
题目
1.删除顺序表中值最小的元素,并由最后一个元素填补
bool Del_Min(SqList &L, ElemType &value){
//删除顺序表L中值最小的结点,并通过引用型参数value返回其值
//如果删除成功,返回true;否则,返回false
if(L.length == 0)
return false;
value = L.data[0];
int pos = 0; //最小元素下标
for(int i = 1; i < L.length; i++)
if(L.data[i] < value){
value = L.data[i];
pos = i;
}
L.data[pos] = L.data[L.length-1]; //填补
L.length--;
return true;
}
2.逆置顺序表,空间复杂度O(1)
扫描顺序表L前半部分,将元素L.data[i](0<= i <L.length/2)与其后半部分对应的元素L.data[L.length-i-1]交换。
void Reverse(SqList &L){
ElemType temp;
for(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;
}
}
3.长度为n的顺序表L,删除所有值为x的数据元素,时间复杂度O(n),空间复杂度O(1)
解法一:用k记录顺序表L中不等于x的元素个数(即需要保留的元素个数),边扫描L边统计k,并将不等于x的元素向前放置k位置上,最后修改L的长度
void del_x_1(SqList &L, ElemType x){
//删除顺序表L中所有值为x的数据元素
int k = 0; //记录值不等于x的元素个数
for(i = 0; i < L.lentgh; i++)
if(L.data[i] != x){
L.data[k] = L.data[i];
k++;
}
L.length = k;
}
解法二:用k记录顺序表L中等于x的元素个数,边扫描L边统计k,并将不等于x的元素前移k个位置,最后修改L的长度
void del_x_2(SqList &L, ElemType x){
int k = 0, i = 0; //k记录等于x的元素的个数
while(i < L.length){
if(L.data[i] == x)
k++;
else
L.data[i-k] = L.data[i];
i++;
}
L.length = L.length-k;
}
4.从有序顺序表中删除其值在给定值s与t之间(包含s和t,要求s<t)的所有元素
因为是有序表,所以删除的元素必然是相连的整体。
算法思想:先寻找值大于等于s的第一个元素(第一个要删除的元素),然后寻找值大于t的第一个元素(最后一个要删除的元素的下一个元素),最后直接将后面的元素前移。
bool Del_s_t2(SqList &L, ElemType s, ElemType t){
//删除有序表L中值在s与t之间的所有元素
int i, j;
if(s >= t || L.length == 0)
return false;
for(i = 0; i<L.length && L.data[i]<s; i++); //寻找值>=s的第一个元素
if(i >= L.length)
return false; //所有值均小于s,则返回
for(j = i; j<L.length && L.data[j]<=t; j++); //寻找值>t的第一个元素
for(; j < L.length; i++, j++)
L.data[i] = L.data[j]; //前移,填补被删除元素位置
L.length = i+1;
return true;
}
5.从顺序表中删除其值在给定值s与t之前(包含s和t,要求s<t)的所有元素
算法思想:从前向后扫描顺序表L,用k记录元素值在s到t之间的元素的个数(初始k=0)。对于当前扫描的元素,若其值不在s到t之间,则向前移动k个位置;否则,k++。不在s到t之间的元素仅移动一次,所以效率高。
与第3题解法二,思想相同。
bool Del_x_t(SqList &L, ElemType s, ElemType t){
//删除顺序表L中值在s与t之间的所有元素
int i, k = 0;
if(L.length==0 || s>=t)
return false; //非法
for(i = 0; i < L.length; i++){
if(L.data[i]<s || L.data[i]>t)
L.data[i-k] = L.data[i]; //当前元素前移k个位置
else
k++;
} //for
L.length -= k;
return true;
}
6.从有序表中删除所有值重复的元素,使表中所有元素的值均不同
算法思想:因为是有序表,值相同的元素一定在连续的位置上。用类似于直接插入排序的思想,初始时将第一个元素看做非重复有序表;之后依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,如果相同则继续向后判断,如果不同则插入到前面的非重复有序表的最后,直到判断到表尾为止。
bool Del_Same(SeqList &L){
if(L.length == 0)
return false;
int i, j; //i存储第一个不相同的元素,j工作指针
for(i = 0, j = 1; j < L.length; j++)
if(L.data[i] != L.data[j])
L.data[++i] = L.data[j];
L.length = i+1;
return true;
}
删除无序顺序表中重复的元素?时间复杂度O(n)?Hash表?
7.将两个有序顺序表合并成一个新的有序顺序表,并由函数返回结果顺序表
算法思想:首先,按顺序不断取下两个顺序表表头较小结点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面
bool Merge(SeqList A, SeqList B, SeqList &C){
//合并有序顺序表A与B成为一个新的有序顺序表C
if(A.length + B.length > C.maxSize)
return false; //大于顺序表的最大长度
int i = 0, j = 0, k = 0;
while(i<A.length && j<B.length){
if(A.data[i] <= B.data[j])
C.data[k++] = A.data[i++];
else
C.data[k++] = B.data[j++];
}
while(i < A.length) C.data[k++] = A.data[i++];
while(j < B.length) C.data[k++] = B.data[j++];
C.length = k;
return true;
}
8.线性表(a1,a2,a3,…,an)中元素递增有序且按顺序存储。要求用最少时间在表中查找数值为x的元素,若找到将其与后继元素位置相交换,若找不到将其插入表中并使表中元素仍递增有序
算法思想:折半查找
void SearchExchangeInsert(ElemType A[], ElemType x){
int low = 0, high = n-1, mid;
while(low <= high){
mid = (low+high)/2; //找中间位置
if(A[mid] == x) break; //找到x,退出while循环
else if(A[mid] < x) low = mid + 1; //到中点mid的右半部分去查
else high = mid - 1; //到中点mid的左半部分去查
}
//下面两个if语句只会执行一个
if(A[mid]==x && mid!=n-1){ //若最后一个元素与x相等,则不存在与其后继交换的操作
t = A[mid];
A[mid] = A[mid+1];
A[mid+1] = t;
}
if(low > high){ //查找失败
for(i = n-1; i > high; i--)
A[i+1] = A[i]; //后移元素
A[i+1] = x; //插入x
}
}
9.设将n(n>1)个整数存放到一个一维数组R中。将R中保存的序列循环左移p(0<p<n)个位置,即将R中的数据由(X0,X1,…,Xn-1)变换为(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)
算法思想:可将此问题看成把数组ab转换成数组ba(a代表数组的前p个元素,b代表余下的n-p个元素),先将a逆置得到a-1b,再将b逆置得到a-1b-1,最后将整个a-1b-1逆置得到ba。
void Reverse(int R[], int from, int to){
int i, temp;
for(i = 0; i < (to-from+1)/2; i++){
temp = R[from+i];
R[from+i] = R[to-i];
R[to-i] = temp;
}
} //Reverse
void Converse(int R[], int n, int p){
Reverse(R, 0, p-1);
Reverse(R, p, n-1);
Reverse(R, 0, n-1);
}
10.一个长度为L(L>=1)的升序序列S,处在第 ⌈ L / 2 ⌉ \lceil L/2 \rceil ⌈L/2⌉个位置的数称为S的中位数。例如,若序列S1=(11,13,15,17,19),则S1的中位数是15,两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若S2=(2,4,6,8,20),则S1和S2的中位数是11。现在有两个等长升序序列A和B,找出两个序列A和B的中位数
算法思想:
分别求两个升序序列A、B的中位数,设为a和b,求序列A、B的中位数过程如下:
1)若a=b,则a或b即为所求中位数,算法结束。
2)若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
3)若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中,重复过程1)、2)、3),直到两个序列中均只含一个元素时为止,较小者即为所求的中位数。
int M_Search(int A[], int B[], int n){
int s1=0, d1=n-1, m1, s2=0, d2=n-1, m2;
//分别表示序列A和B的首位数、末尾数和中位数
while(s1!=d1 || s2!=d2){
m1 = (s1+d1)/2;
m2 = (s2+d2)/2;
if(A[m1] == A[m2])
return A[m1]; //满足条件1)
if(A[m1] < B[m2]){ //满足条件2)
if((s1+d1)%2 == 0){ //若元素个数为奇数
s1 = m1; //舍弃A中间以前的部分且保留中间点
d2 = m2; //舍弃B中间以后的部分且保留中间点
}
else{ //元素个数为偶数
s1 = m1+1; //舍弃A中间点及中间点以前部分
d2 = m2; //舍弃B中间点以后部分且保留中间点
}
}
else{ //满足条件3)
if((s2+d2)%2 == 0){ //若元素个数为奇数
d1 = m1; //舍弃A中间点以后的部分且保留中间点
s2 = m2; //舍弃B中间点以前的部分且保留中间点
}
else{ //元素个数为偶数
d1 = m1; //舍弃A中间点以后部分且保留中间点
s2 = m2+1; //舍弃B中间点及中间点以前部分
}
}
}
return A[s1] < B[s2] ? A[s1] : B[s2];
}
11.已知一个整数序列A(a0,a1,…,an-1)其中0<=ai<n(0<=i<n)。若存在ap1=ap2=…=apm=x且m>n/2(0<=pk<n,1<=k<=m),则称x为A的主元素。例如A=(0,5,5,3,5,7,5,5),则5为主元素;又如A=(0,5,5,3,5,1,5,7),则A没有主元素。假设A中的n个元素保存在一个一维数组中,找出A的主元素。若存在,则输出主元素;否则输出-1
算法思想:算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确认Num是否是主元素。
算法可以分为以下两步:
1)选取候选的主元素:依次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数为1;若遇到的下一个整数仍等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素。
2)判断c中元素是否是真的主元素;再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则,序列中不存在主元素。
int Maiority(int A[], int n){
int i, c, count = 1; //c用来保存候选主元素,count用来计数
c = A[0]; //设置A[0]为候选主元素
for(i = 1; i < n; i++) //查找候选主元素
if(A[i] == c)
count++; //对A中的候选主元素计数
else
if(count > 0) //处理不是候选主元素的情况
count--;
else{ //更换候选主元素,重新计数
c = A[i];
count = 1;
}
if(count > 0)
for(i=count=0; i < n; i++) //统计候选主元素的实际出现次数
if(A[i] == c)
count++;
if(count > n/2) return c; //确认候选主元素
else return -1; //不存在主元素
}