线性表的逻辑特征:至少含一个元素,则只有唯一的开始元素和终端元素,除了开始元素外其他元素有且仅有一个前驱结点
不同实际问题当中,数据元素的类型不同,但同一个线性表当中所有元素具有相同的类型,数据项一样,数据项类型一样
线性表基本运算:
初始化InitList(L) 作用创建一个空表
销毁线性表DestroyLIst(L)释放L内存
求线性表长度
求线性表中第i个元素GetElem(L,i,e)
按值查找位置(L,e)
插入元素:在某个位置插入数据InsElem(L,x,i)
删除元素在某个未知删除元素DelElem(L,e)
输出元素DispList(L)
线性表的存储结构主要分为:顺序存储和链式存储 前者简称为顺序表
线性表相当于一个特殊的数组这个‘数组’拥有一个记录其长度的元素:length
由于顺序表采用数组存放元素,而数组具有随机存取特性,所以顺序表局有随机存取特性
#include<iostream> using namespace std; //定义线性表中数组的长度 #define MaxSize 100 //定义数组类型 typedef int ElemType; //定义自定义类型结构体 typedef struct{ ElemType data[MaxSize]; int length; } List; //初始化线性表 void InitList(List &L){ L.length=0; } //销毁线性表 void DestroryList(List L){} //获取长度 int GetLength(List &L){ return L.length; } //在某个位置插入元素 函数返回类型为int为了判断是否插入成功 int InsElem(List &L,ElemType a,int r){ if(r<1||r>L.length+1){ return 0; } int len=GetLength(L); //将r-1到L.length-1的的元素均往后挪一个位置 for(int i=len;i>r-1;i--){//这里可能相对来说难理解 因为要在逻辑位置r处插入元素 应该将r-1和r-1之后的元素往后挪 L.data[r]=L.data[r-1] L.data[i]=L.data[i-1]; } L.data[r-1]=a; //插入元素后长度加一 L.length++; return 1; } //删除某个位置的元素 int DelElem(List &L,int r){ //判断位置是否正确 if(r<1||r>L.length){ return 0; } //将r-1到L.length-1的元素前移一位 最后执行L.data[L.length-2]=L.data[L.length-1] for(int i=r;i<L.length;i++){ L.data[i-1]=L.data[i]; } L.length--; return 1; } //返回某个元素的位置 int LocaElem(List &L,ElemType a){ int i=0; //这里可以使用while循环或者for循环 while(L.data[i]!=a&&i<L.length){ i++; } //如果循环完了还没找到元素返回0否则返回逻辑序号 if(i>=L.length){ return 0; }else{ return i+1; } } //输出元素 void DisList(List &L){ if(L.length==0){ cout<<"当前元素为空"; }else{ cout<<"线性表的元素如下:"<<endl; for(int i=0;i<L.length;i++){ cout<<L.data[i]<<" "; } cout<<endl; } } //返回某个位置的元素的值 int GetElem(List &L,ElemType a,int r){ if(r>L.length||r<1){ return 0; }else{ a=L.data[r]; return 1; } } int main(){ /* 实现功能用一个线性表 :初始化InitList :销毁线性表 DestroryList :在某个位置插入元素 InsElem :删除元素DelElem :返回某个元素的位置LocaElem :显示元素DisList :返回长度 :返回地某个位置的元素 */ //声明线性表 List L; ElemType a; //初始化线性表 InitList(L); //插入元素 cout<<"插入元素1,3,5,12"<<endl; InsElem(L,1,1); InsElem(L,3,2); InsElem(L,5,3); InsElem(L,12,4); DisList(L); cout<<"返回第3个位置的元素"<<endl; GetElem(L,a,2); cout<<a<<endl; cout<<"长度"<<GetLength(L)<<endl; cout<<"获取元素3的位置"<<endl; cout<<LocaElem(L,3)<<endl; cout<<"删除第2个元素"<<endl; if(DelElem(L,2)){ cout<<"删除成功"<<endl; }else{ cout<<"删除失败"<<endl; } cout<<"输出所有元素"<<endl; DisList(L); cout<<"销毁<<endl"; DestroryList(L); return 0; }
结果如下:
将上面的顺序表的基本运算分装成一个头文件 48.h
下面使用 注意主函数只能有一个
从r开始删除n个元素
#include "48.h" //从第r个元素开始删除n个元素 int delElems(List &L,int r,int n){ //判断参数是否正确 if(r<1||n<1||r+n-1>L.length){ return 0; } //从r+n-1开始向前移动n个位置 注意一个问题 从r开始删除n个元素 实际上逻辑序列删除 r 到 r+n-1 也就是实际顺序 r-1 到r+n-2 r+n-1不删除 for(int j=r+n-1;j<L.length;j++){ L.data[j-n]=L.data[j]; } L.length-=n; return 1; } int main(){ List L; InitList(L); InsElem(L,1,1); InsElem(L,3,2); InsElem(L,11,3); InsElem(L,12,4); InsElem(L,11,5); InsElem(L,21,6); InsElem(L,31,7); InsElem(L,13,8); InsElem(L,155,9); InsElem(L,11,10); InsElem(L,1,7); InsElem(L,10,4); DisList(L); delElems(L,2,5); DisList(L); return 0; }
//将所有奇数放到偶数前面 void move(List &L){ int i=0; int j=L.length; //一个从前面找偶数 从后面找奇数 当中指导位置重合或者相交 while(i<=j){ while(L.data[i]%2==1) i++; while(L.data[j]%2==0) j--; if(i<j){ ElemType a=L.data[i]; L.data[i]=L.data[j]; L.data[j]=a; } } }
解析:
一个从线性表开头开始找偶数 一个从尾巴 开始向前找奇数 当找到时判断此时i 和 j 的位置条件如果满足就交换顺序 一般只有两种情况:
前面的奇数多或者前面的偶数多(将少部分的奇数放到偶数前面)
二路归并
3.在合并两个有序数组。如图2
(1) 合并时,有两个指针分别指向有序数组A和B的起始元素,比较指针所指元素的大小,如果A[i]较小,则将A[i]
存入数组C中,并且将i后移。循环比较i和j所指的元素。
(2)当一个数组A的元素全部排之后,数组B中的指针就并没有指向B的末尾位置,将B中剩余元素全部存入到C中。
//合并两个顺序表到一个线性表当中从小排到大的有序顺序表 void merge(List &L1,List &L2,List &rst){ int i=0;//L1下标 int j=0;//L2下标 int k=0;//rst的下标 while(i<L1.length&&j<L2.length){ if(L1.data[i]>L2.data[j]){ rst.data[k]=L2.data[j]; k++; j++; }else if(L1.data[i]<L2.data[j]){ rst.data[k]=L1.data[i]; k++; i++; }else{ rst.data[k]=L1.data[i]; k++; i++; rst.data[k]=L2.data[j]; k++; j++; } } //到这一步将剩下的直接放在上面 while(i<L1.length){ rst.data[k]=L1.data[i]; k++; i++; } while(j<L2.length){ rst.data[k]=L2.data[j]; k++; j++; } rst.length=k; }
还有一个相似的例子:已知有两个递增有序表A和B 设计算法将A和B的所有公共元素产生一个顺序表C 原理同上