数据结构——线性表(顺序存储)

线性表的顺序存储

顺序存储的线性表称顺序表

顺序存储
特点
建立
方式
1地址连续,2随机存取,3顺序存储
1首地址,2储存空间大小,3表长
1静态,2动态

物理顺序存储方式

数组下标 顺序表 内存地址
0 a1 LOC(A)
1 a2 LOC(A)+sizeof(Elemtype)
i-1 ai LOC(A)+(i-1)*sizeof(Elemtype)
n-1 an LOC(A)+(n-1)*sizeof(Elemtype)
MaxSize-1 LOC(A)+(MaxSize-1)*sizeof(Elemtype)

优缺点

优点 缺点
存储密度大,不需要为元素的逻辑关系额外开辟空间 插入和删除操作需要移动大量元素,平均时间复杂度O(n)
随机存取,可快速存取表中任一元素 对存储要求高,会出现存储碎片

(注意,随机存取这个概念很重要,它不是存储)

顺序存储
1、线性表是一种逻辑结构存储单元地址连续;顺序表和链表是储存结构;
2、线性表的顺序表示

//线性表的静态分配
#define MaxSize 50	//定义线性表的最大长度
typedef struct{//注意:线性表元素位序从1开始,而数组从0开始
	ElemType data[MaxSize];
	int length;
}SqList;
//线性表的动态分配
#define InitSize 100	//定义表的初始长度
typedef struct{
	ElemType *data;
	int MaxSize,length;
}SeqList;

SeqList L;//初始化一个叫做L的线性表
L.data=new ElemType[InitSize];
//动态分配虽然使用指针,但是它不是链式存储,而是顺序存储,因为开辟的空间是连续的

3、顺序表的操作
插入操作
在顺序表第i个元素的位置插入一个新元素e

bool ListInsert(SqList &L,int i,Elemtype e){
	if(i<1||i>L.length+1)//判断i的范围是否有效
		return false;//顺序表首位是1,末位是length,length+1则正好插到最后
	if(L.length>=MaxSize)//储存空间已满,不可插入
		return false;
	for(int j=L.length;j>=i;j--)//注意,这里从length开始减少,因为数组下标从0开始
		L.data[j]=L.data[j-1];
	L.data[i-1]=e;//注意,这里i-1,因为数组下标从0开始
	L.length++;
	return true;
}

删除操作
删除顺序表第i个元素,并把删除元素的值赋给e,返回出来

bool ListDelete(SqList &l,int i,Elemtype &e){
	if(i<1||i>L.length)
		return false;
	e=L.data[i-1];
	for(int j=i;j<L.lenth;j++)
		L.data[j-1]=L.data[j];
	L.length--;
	return true;
}

按值查找
在顺序表L中查找第一个元素为e的元素,并返回其位序

int LocateElem(Sqlist L,ElemType e){
	int i;
	for(i=0;i<L.length;i++){
		if(L.Data[i]==e){
			return i+1;
		}
	}
}

习题
1、从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删元素的值。空出的位置又最后一个元素填补,若顺序表为空则显示错误信息并退出运行。

bool Del_Min(SqList &L,ElemType &value){
//删除顺序表L中最小元素节点,并通过引用型参数value返回其值
//若删除成功,则返回true;否则返回false
	if(L.length==0)return false;
	value=L.data[0];//先给value一个初值 
	int pos=0;
	for(int i=0;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、设计一个高效算法,将顺序表L中所有元素逆置,要求算法的空间复杂度为O(1)。

void Reverse(SqList &L){
	if(L.length==0)return false;
	if(L.length==1)return true;
	ElemType temp;
	for(int i=0;i<L.length/2;i++){
		temp=L.data[i];
		L.data[i]=L.data[L.length-1-i];
		L.data[L.length-1-i]=temp;
	}
	return true;
}

3、对于长度为n的顺序表L,编写一个时间复杂度为O(n)、空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的元素。

void Del_x(SqList &L,ElemType x){
	int k=0,i=0;
	for(;i<L.length;i++){
		if(L.data[i]!=x){
			L.data[k]=L.data[i];
			k++;//用k记录不等于x的元素个数 
		}
	}
	L.length=k;
}
程序的执行过程:

假设起始数列  [a]	[b]	[x]	[c]	[d]	[x]	[e]

k=0 i=0 判断真
[a]	[b]	[x]	[c]	[d]	[x]	[e]
k=1 i=1 判断真
[a]	[b]	[x]	[c]	[d]	[x]	[e]
k=2 i=2 判断假
[a]	[b]	[x]	[c]	[d]	[x]	[e]
k=2 i=3 判断真
[a]	[b]	[c]	[c]	[d]	[x]	[e]
k=3 i=4 判断真
[a]	[b]	[c]	[d]	[d]	[x]	[e]
k=4 i=5 判断假
[a]	[b]	[c]	[d]	[d]	[x]	[e]
k=4 i=6 判断真
[a]	[b]	[c]	[d]	[e]	[x]	[e]
k=5 for循环结束

L.length = 5
[a]	[b]	[c]	[d]	[e]

这段代码所给出的消除方法我确实想不到,背过就好

4、从有序表中删除其值在给定值s与t之间(要求s 注意,此题是有序表

bool Del_s_t(SqList &L,ElemType s,Elemtype t){
    int i,j;
    if(s>=t||L.length==0)//如果s和t的值不合适或线性表为空,返回错误信息
        return false;
    for(i=0;i<L.length&&L.data[i]<s;i++);//寻找值大于等于s的第一个元素
    if(i>=L.length)//没有合适的s,返回错误信息
        return false;
    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;
    return true;
}
[a] [b] [s] [c] [d] [t] [e]
经过i的for循环,i=2
经过j的for循环,j=6
推演第三个for循环
i=2 j=6 判断真
[a] [b] [e] [c] [d] [t] [e]
i=3 j=7 判断假 推出循环
L.length=3
[a] [b] [e]

5、从顺序表中删除其值在给定值s与t之间(要求s

bool Del_s_t(SqList &L,Elemtype s,Elemtype 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){
            k++;
        }else{
             L.data[i-k]=L.data[i];
        }
    }
    L.length-=k;
    return true;
}
[a] [b] [s] [c] [d] [t] [e]
i=0 k=0 判断假
[a] [b] [s] [c] [d] [t] [e]
i=1 k=0 判断假
[a] [b] [s] [c] [d] [t] [e]
i=2 k=0 判断真
[a] [b] [s] [c] [d] [t] [e]
k=1 i=3 判断假
[a] [b] [c] [c] [d] [t] [e]
k=1 i=4 判断假
[a] [b] [c] [d] [d] [t] [e]
k=1 i=5 判断真
[a] [b] [c] [d] [d] [t] [e]
k=2 i=6 判断假
[a] [b] [c] [d] [e] [t] [e]
k=2 i=7 循环结束
L.length=L.length-k

6、从有序表中删除所有其值重复的元素,使表中所有元素均不相同。

bool Del_Same(SqList &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;
}
[a] [b] [b] [c] [d] [e] [e]
i=0 j=1 判断真
[a] [b] [b] [c] [d] [e] [e]
i=1 j=2 判断假
[a] [b] [b] [c] [d] [e] [e]
i=1 j=3 判断真
[a] [b] [c] [c] [d] [e] [e]
i=2 j=4 判断真
[a] [b] [c] [d] [d] [e] [e]
i=3 j=5 判断真
[a] [b] [c] [d] [e] [e] [e]
i=4 j=6 判断假
[a] [b] [c] [d] [e] [e] [e]
i=4 j=7 退出循环
L.length=i+1

7、把两个有序表合并为一个新的有序表,并由函数返回结果顺序表。
典型方法,牢固掌握。

bool Merge(SqList A,SqList B,SqList &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;
}
注意
while(i

8、已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3…am)和(b1,b2,b3…bn)。试编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3…bn)放在前面。
这是王道给出的代码,确实非常高效:

typedef int DataType;//方便维护和修改
void Reverse(DataType A[],int left,int right,int arraySize){
    if(left>=right||right>=arraySize)
        return;//如果left或right的参数设置有问题,退出函数
    int mid=(left+right)/2;
    for(int i=0;i<mid-left;i++){//逐个进行调换
        DataType temp=A[left+i];
        A[left+i]=A[right-i];
        A[right-i]=temp;
    }
}
void Exchange(DataType A[],int m,int n,int arraySize){
    Reverse(A,0,m+n-1,arraySize);
    Reverse(A,0,n-1,arraySize);
    Reverse(A,n,m+n-1,arraySize);
}

我要是自己做的话,肯定会新开一个数组

9、线性表(a1,a2,a3…an)中的元素递增有序且按顺序存储于计算机内。要求设计一算法,完成用最少时间在表中查找数值为x的元素,若找到则将其与后继元素位置相交换,若找不到则将其插入表中并使表中元素仍递增有序。

//使用折半查找法比较合适
void SearchExchangeInsert(ElemType A[],ElemType x){
    int low=0,high=n-1,mid,i,temp;//这里,n就是线性表的长度
    while(low<=high){
        mid=(low+high)/2;
        if(A[mid]==x) break;//如果可以找到这个x退出循环
        else if(A[mid]<x) low=mid+1;//如果中值比x小,循环体下次研究后半部分
        else high=mid-1;//如果中值比x大,研究上半部分
    }//下面两个if语句只会执行一个
    if(A[mid]==x&&mid!=n-1){//若找到x,与后继元素的值交换
        temp=A[mid];A[mid]=A[mid+1];A[mid+1]=temp;
    }
    if(low>high){//如果查找失败,即数组里面没有x,插入数据x
        for(i=n-1;i>high;i--)//high正好是应插入x的前一个位置
        	A[i+1]=A[i];//元素后移
        A[i+1]=x;//这时,x==high
    }
}

10、【2010统考真题】设将n(n>1)个整数存放到一维数组R中。设计一个在时间和空间都尽可能高效的算法,将R中保存的序列循环左移p(0 由(X0,X1,…,Xn-1)变换为(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)。
要求:
1)给出算法基本设计思想
2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释
3)说明算法的时间复杂度和空间复杂度

ab-->ba
ab->a-1b->a-1b-1->(a-1b-1)-1->ba
void Reverse(int R[],int from,int to){
	int i,temp;
	for(i=0;i<(to-from+1);i++){
		temp=R[from+i];
		R[from+i]=R[to-i];
		R[to-i]=temp;
	}
}
void Converse(int R[],int p,int n){
	Reverse(R,0,p-1);
	Reverse(R,p,n-1);
	Reverse(R,0,n-1);
}
时间复杂度O(n),空间复杂度O(1)

11、【2011统考真题】一个长度为L(L>=1)的升序序列S,处在第L/2个位置的数称为S的中位数,两个序列的中位数是含它们所有元素的升序序列的中位数。现有等长两个升序序列A和B,试设计一个在时间和空间都尽可能高效的算法,找出AB的中位数。
要求:
1)给出算法基本设计思想
2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释
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]==B[m2])
            return A[m1];
        if(A[m1]<B[m2]){
            if((s1+d1)%2==0){
                s1=m1;
                d2=m2;
            }else{
                s1=m1+1;
                d2=m2;
            }
        }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];//三目运算
}

你可能感兴趣的:(数据结构,线性表,c++)