知识框架
基本操作代码
#include <iostream>
using namespace std;
/**
* 1.顺序表的定义
* 两种方式:静态分配和动态分配
*/
//1.1静态分配
#define MAXSIZE 50 //定义线性表的最大长度
typedef struct {
int data[MAXSIZE];//ElemType data[MaxSize] 顺序表的元素
int length; //顺序表的当前长度
}SqList;//顺序表的类型定义
//1.2动态分配
#define InitSize 5 //表长度的初始定义
typedef struct {
int *data; //指示动态分配数组的指针
int MaxSize; //数组的最大容量
int length; //数组的当前长度
}SeqList;
/**
* 2.顺序表的基本操作:以动态
* 创建(初始化) 销毁 插入 删除 按值查找 打印输出
*/
//2.1创建
void InitList(SeqList &L){
//用malloc函数申请一片连续的存储空间
L.data = (int *) malloc(InitSize * sizeof (int));
L.length = 0;
L.MaxSize = InitSize;
}
//2.2销毁
void DestroyList(SeqList &L){
free(L.data);
}
//2.3增加动态数组的长度
void IncreaseSize(SeqList &L,int len){
//指针p指向data的内存区域
int *p = L.data;
//data重新开辟一片更大的存储空间
L.data = (int *) malloc( (L.MaxSize+len) * sizeof (int));
//把原来的数据放到新开辟的内存空间来
for (int i = 0; i < L.length; ++i) {
L.data[i] = p[i];
}
//扩容后改变原来的最大的容量
L.MaxSize = L.MaxSize + len;
//释放掉原来开辟的区域
free(p);
}
//2.4插入
bool ListInsert(SeqList &L,int i,int e){
//判断i的范围是否有效
if (i < 1||i > L.length+1){
return false;
}
//第i个以后的元素(包含第i个)都往后移一位【从最后一个元素开始移】
for (int j = L.length; j >= i; --j) {
L.data[j] = L.data[j-1];
}
//把e放入第i个
L.data[i-1] = e;
//长度+1
L.length++;
return true;
}
//2.5删除:删除顺序表中第i个元素,用引用变量e返回
bool ListDeleted(SeqList &L,int i,int &e){
//判断位置i的合法性
if (i > L.length||i <= 0){
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 true;
}
//2.6按值查找
int LocatedElem(SeqList L,int e){
for (int i = 0; i < L.length; ++i) {
if (L.data[i] == e){
return i+1;
}
}
return -1;
}
//2.7打印输出
void PrintData(SeqList L){
for (int i = 0; i < L.length; ++i) {
cout<<L.data[i]<<" ";
}
cout<<"元素个数:"<<L.length<<" 容量:"<<L.MaxSize;
cout<<endl;
}
int main() {
//创建顺序表L
SeqList L;
InitList(L);
//赋值
for (int i = 0; i < L.MaxSize; ++i) {
L.data[i] = i;
L.length++;
}
//打印
PrintData(L);
//扩容
IncreaseSize(L,5);
PrintData(L);
//插入
ListInsert(L,3,9);
PrintData(L);
//按值查找
int l = LocatedElem(L,4);
cout<<"位置:"<<l<<endl;
//删除
int e = -1;
ListDeleted(L,4,e);
PrintData(L);
//销毁
DestroyList(L);
return 0;
}
注意点
- 存取方式是指读写方式。顺序表是一种支持随机存取的存储结构,根据起始地址加上元素的序号,可以很方便地访问任意一个元素。
- 顺序表:逻辑上相邻的数据元素物理上也相邻。
应用题
- 从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删除元素的值。空出的位置由最后一个元素填补,若顺序表为空则显示出错信息并退出运行。
#include <iostream>
using namespace std;
typedef struct {
int *data;
int length;
int MaxSize;
}SeqList;
/**
* 1.从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删除元素的值。
* 空出的位置由最后一个元素填补,若顺序表为空则显示出错信息并退出运行。
*/
bool Del_Min(SeqList &L,int &e){
if (L.length == 0){
return false;
}
//找到最小值的下标
int min_index = 0;
for (int i = 0; i < L.length; ++i){
if (L.data[i] < L.data[min_index]){
min_index = i;
}
}
//e返回删除的值
e = L.data[min_index];
//最后一个元素填补删除的值
L.data[min_index] = L.data[L.length-1];
return true;
}
int main() {
SeqList L;
L.data = (int *) malloc(5*sizeof (int));
L.MaxSize = 5;
L.data[0] = 8;
L.data[1] = 2;
L.data[2] = 4;
L.data[3] = 5;
L.data[4] = 3;
L.length = 5;
int e = -1;
if (Del_Min(L,e)){
for (int i = 0; i < L.length; ++i){
cout<<L.data[i]<<" ";
}
}
cout<<endl;
cout<<e<<endl;
return 0;
}
- 设计一个高效算法,将顺序表L的所有元素逆置,要求算法的空间复杂度为O(1)
#include <iostream>
using namespace std;
typedef struct {
int *data;
int length;
int MaxSize;
}SeqList;
/**
*2. 设计一个高效算法,将顺序表L的所有元素逆置,要求算法的空间复杂度为O(1)
*/
void Reverse(SeqList &L){
//扫描顺序表L的前半部分元素L.data[i] (0<=i<L.length/2)
// 将其与后半部分的对应元素L.data[L.length-1-i]进行交换。
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;
}
}
int main() {
SeqList L;
L.data = (int *) malloc(5*sizeof (int));
L.MaxSize = 5;
L.data[0] = 1;
L.data[1] = 2;
L.data[2] = 3;
L.data[3] = 4;
L.data[4] = 5;
L.length = 5;
Reverse(L);
for (int i = 0; i < L.length; ++i){
cout<<L.data[i]<<" ";
}
cout<<endl;
return 0;
}
- 顺序表–对长度为n的顺序表L,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素。
/**
* 3.顺序表--对长度为n的顺序表L,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,
* 该算法删除线性表中所有值为x的数据元素
*/
//用k记录线性表L中 不等于 x的个数,边扫描,便统计k,不等于 x的元素移动到第k个位置上
void del_x_1(SeqList &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++;
}
}
L.length = k;
}
- 从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,如果s或t 不合理或者顺序表为空则显示出错信息并退出运行。
/**
* 4. 从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,
* 如果s或t 不合理或者顺序表为空则显示出错信息并退出运行。
*/
//注意是有序表,找到s到t这一段的元素删除即可
bool del_s_t(SeqList &L,int s,int t){
if (s >= t || L.length == 0){
return false;
}
int m=0,n=0;
for (int i = 0; i < L.length; ++i){
if (L.data[i] > s){
m = i;
break;
}
}
if ( m == 0){
return false; //所有元素都小于s
}
for (int i = m; i < L.length; ++i){
if (L.data[i] > t){
n = i;
break;
}
}
//删除下标m~(n-1)
for (int i = n; i < L.length; ++i){
L.data[m] = L.data[i];
m++;
}
L.length = m;
return true;
}
- 从顺序表中删除其值在给定值s与t之间(包含s和t,要求s<t)的所有元素,如果 s或t不合理或者顺序表为空则显示出错信息并退出运行。
/**
* 5. 从顺序表中删除其值在给定值s与t之间(包含s和t,要求s<t)的所有元素,
* 如果 s或t不合理或者顺序表为空则显示出错信息并退出运行。
*/
bool del_s_t2(SeqList &L,int s,int t){
if (s>=t || L.length == 0){
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;
return true;
}
- 从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
/**
* 6. 从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
*/
//有序
bool no_repeat(SeqList &L){
if(L.length == 0){
return false;
}
int k = 1;//记录合格元素个数,默认下标为0是不重复元素
int p = L.data[0];//记录上一个元素的值
for (int i = 1; i < L.length; ++i){
//当前元素不等于它前一个元素(合格)
if (L.data[i] != p){
L.data[k] = L.data[i];
k++;
p = L.data[i];
}
}
L.length = k;
return true;
}
(补充:从顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。无序)
- 将两个有序的顺序表合并成一个新的有序顺序表,并由函数返回结果顺序表。
SeqList merge(SeqList &L1,SeqList &L2){
SeqList L;
L.data = (int *) malloc(sizeof (int )*(L1.length+L2.length));
int i = 0; //记录L1的位置
int j = 0; //记录L2的位置
int k = 0; //记录L的位置
while (i < L1.length && j <L2.length){
if (L1.data[i] < L2.data[j]){
L.data[k] = L1.data[i];
i++;
k++;
} else {
L.data[k] = L2.data[j];
j++;
k++;
}
}
while (i < L1.length){
L.data[k] = L1.data[i];
i++;
k++;
}
while (j <L2.length){
L.data[k] = L2.data[j];
j++;
k++;
}
L.length = k;
return L;
}
int main() {
SeqList L1;
L1.data = (int *) malloc(5*sizeof (int));
L1.MaxSize = 5;
L1.data[0] = 1;
L1.data[1] = 2;
L1.data[2] = 4;
L1.data[3] = 6;
L1.data[4] = 7;
L1.length = 5;
SeqList L2;
L2.data = (int *) malloc(5*sizeof (int));
L2.MaxSize = 5;
L2.data[0] = 3;
L2.data[1] = 8;
L2.data[2] = 9;
L2.data[3] = 11;
L2.data[4] = 14;
L2.length = 5;
SeqList L = merge(L1,L2);
for (int i = 0; i < L.length; ++i){
cout<<L.data[i]<<" ";
}
cout<<endl;
return 0;
}
- 已知在一维数组A[m+n]中依次存放两个线性表(a1, a2, 3,. am)和(b1, b2, b…,bn).将数组中两个顺序表的位置互换,即将(b1,b2,b3,…,bn)放在(a1,a2,a3,…,am)的前面。
/**
* 8. 已知在一维数组A[m+n]中依次存放两个线性表(a1, a2, 3,. am)和(b1, b2, b...,bn).
* 将数组中两个顺序表的位置互换,即将(b1,b2,b3,...,bn)放在(a1,a2,a3,...,am)的前面。
*/
void reverse(int A[],int left,int right,int arraySize){
if (left>=right || right >= arraySize){
return;
}
while (right > left){
int temp = A[left];
A[left] = A[right];
A[right] = temp;
right--;
left++;
}
}
bool exchange(int A[],int n,int m,int arraySize){
//先将A逆置:bn,...,b1,am,...,a1
//在将前n个逆置:b1,..,b2,am,...,a1
//最后将第n+1~m+n个逆置:b1,..,bn,a1,...,am
reverse(A,0,m+n-1,arraySize);
reverse(A,0,n-1,arraySize);
reverse(A,n,n+m-1,arraySize);
}
int main() {
int A[] = {1,3,5,7,2,4,6,8};
exchange(A,4,4,8);
for (int i = 0; i < 8; ++i) {
cout<<A[i]<<" ";
}
return 0;
}
- 线性表(a1,a2,a3,…,an)中元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为x的元素,若找到,则将其与后继元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。
/**
* 9. 线性表(a1,a2,a3,…,an)中元素递增有序且按顺序存储于计算机内。
* 要求设计一个算法,完成用最少时间在表中查找数值为x的元素,
* 若找到,则将其与后继元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。
*/
void search_x(SeqList &L,int x){
//递增有序:采用二分法查找x
int left = 0;
int right = L.length-1;
int mid;
while (right >= left){
mid = (right+left)/2;
if (x > L.data[mid]){
left = mid+1;
} else if (x < L.data[mid]){
right = mid-1;
} else {
//找到了
break;
}
}
//查找成功
if (L.data[mid] == x && mid != L.length-1){
L.data[mid] = L.data[mid+1];
L.data[mid+1] = x;
}
//查找失败
if (left > right){
int i;
for ( i = L.length-1; i > right; --i){
L.data[i] = L.data[i+1];
}
L.data[i+1] = x;
L.length++;
}
}
- 设将n (n>1)个整数存放到一维数组R中,设计一个算法,将R中的序列循环左移P (0<P<n) 个位置,即将R中的数据由{Xo,X1, …, Xn-1}变换为{Xp,Xp+1, … Xn-1, X0,X1, …,Xp-1}。要求:
(1)给出算法的基本设计思想。
(2) 根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
(3)说明你所设计的算法的时间复杂度和空间复杂度。
【博主分析:这题完全可以参考第8题解题思路,即对这个数组进行几轮逆置,本题3轮逆置,第一轮:对整个数组逆置变成{Xn-1, …,Xp,Xp-1,…,X1, X0},第二轮:[0,p-1]逆置,变成{Xp, …,Xn-1,Xp-1,…,X1, X0},第三轮:[n-p,n-1],变成{Xp,Xp+1, … Xn-1, X0,X1, …,Xp-1}】
完整答案:
(1)算法的基本设计思想:可将这个问题视为把数组ab转换成数组ba(a代表数组的前p个元素,b代表数组中余下的n -p个元素),先将a逆置得到
a
−
1
b
a^{-1}b
a−1b,再将b逆置得到
a
−
1
b
−
1
a^{-1}b^{-1}
a−1b−1,最后将整个
a
−
1
b
−
1
a^{-1}b^{-1}
a−1b−1逆置得到
(
a
−
1
b
−
1
)
−
1
{(a^{-1}b^{-1})}^{-1}
(a−1b−1)−1= ba。设Reverse函数执行将数组元素逆置的操作。
(2)
void reverse(int A[],int left,int right){
if (left>=right){
return;
}
while (right > left){
int temp = A[left];
A[left] = A[right];
A[right] = temp;
left++;
right--;
}
}
void Converse(int R[],int n,int p){
reverse(R,0,p-1);
reverse(R,p,n-1);
reverse(R,0,n-1);
}
(3)时间复杂度O(n),空间复杂度O(1).
- 一个长度为L(L≥1)的升序序列S,处在第[L/2]个位置的数称为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++语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度。