目录
数据结构三要素——逻辑结构、物理结构、数据的运算。
逻辑结构------线性表。
物理结构(线性表的实现)------顺序---顺序表、链式---单-双-循环-静态......
线性表:相同数据类型、有限序列。
顺序表(线性表的顺序存储结构):元素的逻辑顺序与物理顺序相同;随机存取(随机访问);存取密度高(每个结点只存储数据元素);拓展容度不方便。
单链表、双链表、循环链表(指针实现)。
静态链表(借助数组实现)。
顺序表
顺序表--静态分配
线性表的顺序存储类型(静态分配数组空间):
#define Maxsize 50
typedef struct{
ElemType data[Maxsize];//元素
int length;//顺序表当前长度
}SqList;//顺序表的类型定义
顺序表--动态分配
线性表的顺序存储类型(动态分配数组空间):
#define InitSize 100 //表长度的初始定义
typedef struct{
ElemType *data; //动态分配数组的指针
int MaxSize,length; //最大容量,当前个数
}SeqList; //动态分配数组顺序表的类型定义
//C的初始动态分配语句,将malloc返回的指针强制转型为elemtype类型
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
//C++初始动态分配语句
L.data=new Elemtype[InitSize];
动态分配并不是链式存储,它同样属于顺序存储结构(物理结构没有变化),依然是随机存取方式,只是分配的空间大小在运行时动态决定。
顺序表--应用
在顺序表L的第i个位置插入新元素-----说明表里第i个是新元素------所以1<=i<=L.length+1。平均时间复杂度为O(n)。
在顺序表L的第i个位置删除元素-----说明表里第i个是被删除元素,用引用变量e返回------所以1<=i<=L.length。平均时间复杂度为O(n)。
T1.把n个整数存放在一维数组R----ab转换为ba
设计思想:把数组ab转换为ba,a为数组中前p个数,b为后n-p个数。设置一个函数reverse将a进行元素逆置,则先后对a、b逆置得到&a,&b(ab的逆置就是&b&a),再将&a&b逆置得到ba。
void reverse(int R[], int from, int to) {
int i, temp; // 定义变量i作为循环索引,temp作为临时交换变量
// 使用for循环来遍历数组R中从索引from到to的部分,并进行逆置
// 注意:to、from是数组下标=元素位序下标-1,从0开始记。
for(i = from; i <= (to - from) / 2; i++) {
// 进行元素交换
temp = R[i]; // 先保存R[i]的值到temp
R[i] = R[to - i]; // 将R[to-i]的值赋给R[i]
R[to - i] = temp; // 最后将temp(即原R[i]的值)赋给R[to-i]
}
}
//为了避免可能的误解并确保逻辑清晰,建议将循环条件改为`i < (to - from + 1) / 2`。这样,在处理元素个数为奇数的情况时,逻辑会更加直观且不易出错。
void converse(int R[],int p,int n){
reverse(R,0,p-1);//时间复杂度为O(p/2)
reverse(R,p,n-1);//时间复杂度为O((n-p)/2)
reverse(R,0,n-1);//时间复杂度为O(n/2)
}
以上算法的时间复杂度为O(n),空间复杂度为O(1)。
T2找出两个等长升序序列A和B的中位数
1.设计思想:逐个比较,直到第L/2个数。(不确定对错)
int compare(int A[],int B[],int n){
//n是A,B序列的长度
int i,j,e;
for(i=1,j=1;i+j-2<n;){
if (A[i-1]<=B[j-1]){
e=A[i-1];
i++;
}
else{
e=B[j-1];
j++;
}
}
return e;
}
法2.参考答案设计思想:
int search(int A[],int B[],int n){
int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2;
//分别表示序列A和B的首位数s=0、末位数d=n-1和中位数m=n/2-0.5
while(s1!=d1||s2!=d2){
m1=(s1+d1)/2; //相当于向下取整了欸。
m2=(s2+d2)/2;
if(A[m1]==B[m2]){
return A[m1];
}
if(A[m1]<B[m2]){
if((s1+d1)%2==0){ //若元素个数为奇数。
s1=m1; //舍弃A中间点以前的部分,且保留中间点
d2=m2; //舍弃B中间点以后的部分,且保留中间点
}
else{
s1=m1+1; //舍弃A中间点以及以前的部分
d2=m2; //舍弃B中间点以后的部分,且保留中间点
}
}
else{
if((s2+d2)%2==0){
d1=m1;
s2=m2;
}
else{
d1=m1;
s2=m2+1;
}
}
}
return A[s1]<B[s2]?A[s1]:B[s2];
}
算法的时间复杂度为O(),空间复杂度为O(1)。
T3含n个整数的数组,找出未出现的最小正整数。
设计思路:要求在时间上尽可能高效,因此采用空间换时间的办法,分配一个用于标记的数组B用来记录A中是否出现了1~n中的正整数。那么B[0]就对应正整数1,B[n-1]就对应正整数N,初始化B中全部为零。
由于A中含有N个整数,因此最小正整数可能返回的值是1~n+1。当A中n个数恰好为1~n时,返回n+1。
当数组A中出现了>=0或>N的值时,会导致1~n中出现空余位置返回,结果必然在1~n中。因此,对于A中出现了>=0或>N的值,可以不采取任何操作。
经过以上分析,可以得出算法流程从A[0]开始遍历A。如果0<A[i]<=n,则令B[A[i]-1]=1,否则不做操作。
对A遍历结束之后,开始遍历数组B。如果能查到第一个满足B[i]==0的下标返回i+1即为结果,说明此时A中未出现的最小正整数在1~n之间。如果B[i]全部不为零,返回i+1,即n+1(跳出循环时,i=n,i+1=n+1)。此时说明A中未出现的最小正整数为n+1。
int findmin(int A[],int n){
int i;
int *B;
B=(int*)malloc(sizeof(int)*n); //分配空间!!!
memset(B,0,sizeof(int)*n); //赋初始值为0!!!
for(i=0;i<n;i++){
if(A[i]>0&&A[i]<=n){ //不能直接写0<A[i]<=n!!!
B[A[i]-1]=1;
}
}
//或者用while
//i=0;
//while(B[i]!=0&&i<n){i++;}
for(i=0;i<n;i++){
if(B[i]==0) break;
}
return i+1;
}
注意分配空间malloc!!!还有赋初始值memset!!!条件用&&
遍历A一次,遍历B一次,两次循环内操作都是O(1)量级,相加就是O(2n)=O(n),时间复杂度为O(n);额外分配了B[n],空间复杂度为O(n)。
T4非空整数集合存储在3个数组中,计算三元组最小距离。
设计思想:
以上是对于三个数的计算方法,即计算最大值和最小值之差的两倍。
思路就是:计算初始最小距离,遍历,更新最小距离。
#define INT_MAX 0x7fffffff
//这一行是一个预处理器指令,用于定义一个常量INT_MAX,其值为0x7fffffff。
int abs_(int a){
if(a<0) return -a;
else return a;
}
bool a_min(int a,int b,int c){
if(a<=b&&a<=c) return ture;
return false;
}
int findmin(intA[],int n1,intB[],int n2,int C[],int n3){
//D_Min用于记录三元组的最小距离,初始值赋为INT_MAX。
int i=0,j=0,k=0,D_Min=INT_MAX,D;
while(i<n1&&j<n2&&k<n3&&D_Min>0){ //这里为什么还需要特别加上D_Min>0?
D=abs_(A[i]-B[j])+abs_(B[j]-C[k])+abs_(C[k]-A[i]);//计算D
if(D<D_Min) D_Min=D;//更新D
if(a_min(A[i],B[j],C[k])) i++;//更新a
else if(a_min(B[j],A[i],C[k])) j++;
else k++;
}
return D_Min;
}
INT_MAX表示什么意思?在32位系统中,这个十六进制数0x7fffffff代表了整型变量所能表示的最大正整数值,即2147483647。通常用来作为算法中一个非常大的初始值或者边界值。
为什么是这样个更新法?(保持较大的两个值不变,将最小的数更新?)结合分析,注意到D的值可以由固定c,更新a(最小值)来计算,而且注意这是一个升序数组,更新a,即把最小值变大了,D会变小!
设n=(),则时间复杂度为O(n),空间复杂度为O(1)。