1.线性表的定义
线性表是具有相同数据结构类型的n(n>=0)个数据元素的有限序列。其中n为表长,当n=0时为空表。其一般的表示方法为:
L=(a 1 ,a 2 ,a 3 ......,a i ,a i+1 ,....a n )
除了第一个元素之外,每个元素有且只有一个直接前驱。除最后一个元素外,每个元素有且只有一个直接后继。
2.线性表的特点:
表中元素的个数有限
表中元素具有逻辑上的顺序性,在序列中各元素排序由先后次序
表中数据元素类型相同
表中数据具有抽象性
线性表是一种逻辑结构,表示元素的一对一的相邻关系。顺序表和链表是指存储结构。
3.线性表的相关操作:
InitList(&L):初始化表。构造一个空的线性表
Length(L):求表长
GetElem(L,i,&e):用e返回L中的第i个数据元素的值
LocateElem(L,e,compare( )):按值查找操作。在表L中查找具有给定关键字值得元素与e相同的元素的位序。
ListInsert(&L,i,e):在L中第i个元素之前插入元素e,表长加1。
ListDelete(&L,i,&e):删除L中第i个数据元素,并用e返回其值,L的长度减1。
4.线性表的存储类型(顺序存储、动态存储)
顺序存储
#define MaxSize 50
typedef struct{
ElemType data[ MaxSize];
int length;
}SqList;
动态存储
#define InitSize 100
typedef struct {
ElemType *data;
int MaxSize,length;
}SqList;
动态分配的语句(C && C++)
C语言: L.data=( Elem Type*)malloc(sizeof( Elem Type)*InitSize);
C++: L.data=new Elem Type[ InitSize];
顺序表的特点:
随机访问、存储密度高、逻辑上相邻物理上元素相邻、插入删除操作需要移动大量元素。
5.顺序表操作
插入操作:采用的思路是首先先判断i的输入是否合法,如果不合法,则返回false,表示插入失败。否则,将第i个元素及之后的元素进行后移一位,插入需要插入的元素e,顺序表长加1,插入成功返回true。
bool ListInsert(SqList &L,int i,ElemType e) {
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 ture;
}
采用动态存储结构进行插入操作:
思路:对要进行插入的操作进行判断,判断插入的i值是否合法,如果不合法,则返回error。对存储空间进行判断,如果满了的话,则增加分配。如果分配失败,则返回溢出。建立新的地址,增加存储容量。同时找到插入的位置,对其后的元素进行后移。
status ListInsert_Sq(SqList &L,int i,ElemType e) {
if(i<1|| i<L.length +1) return error;
//对于存储容量不够进行动态分配。如果失败,建立新的基址,增加容量。
if(L.length>=L.listsize) {
newbase= ( Elem Type*)realloc(L.elem,L.listsize+LISTINCREMENT)*sizeof( Elem Type);
if(!newbase) exit (overflow)
L.elem=newbase;
L.listsize+= LISTINCREMENT;
}
//找到插入的位置q,同时求线性表长度p,对于大于q的元素向后移一位。同时表长加1。
q=&(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;- -p) *(p+1)=*p;
*q=e;
++L.length;
return ok;
} //List Insert_Sq
删除操作:
思路:删除顺序表中第i个元素,成功时返回ture,并将删除元素用变量e返回,否则返回false。先判断i值是否合法,然后对删除的前部分,位序不变,而后面的元素进行地址减1,同时线性表的长度减1。
bool ListDelete(SqList &L,int i,int &e) {
if(i<1||i>L.length)
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 ture;
}
或者:
status ListDelete_Sq(SqList &L,int i, ElemType &e)
if(i<1|| i<L.length +1) return error;
p=&(L.elem[i-1]);
e=*p;
q=L.elem+L.length-1; //注意:由于地址取数组的元素时是第一个地址,所以这样写是正确的
for (++p;p<=q;++p) *(p-1)=*p;
- - L.length;
return ok;
}// ListDelete_Sq
按值查找:
在线性表中查找与e相同的元素,并返回其位序。
int LocateElem(SqList L,ElemType e) {
int i;
for (i=0;i<L.length;i++)
if(L.data[i]==e)
return i+1;
return 0;
}
逆转顺序表中的所有元素:
思路:采用第一个元素与最后一个元素进行对调的方法,第二个与倒数第二个对调,.......。
void Reverse(int A[ ], int n)
{
int i,t;
for (i=0;i<n/2;i++)
{
t=A[ i ];
A[ i ]=A[n-i-1];
A[n-i-1]=t;
}
}
线性表是具有相同数据结构类型的n(n>=0)个数据元素的有限序列。其中n为表长,当n=0时为空表。其一般的表示方法为:
L=(a 1 ,a 2 ,a 3 ......,a i ,a i+1 ,....a n )
除了第一个元素之外,每个元素有且只有一个直接前驱。除最后一个元素外,每个元素有且只有一个直接后继。
2.线性表的特点:
表中元素的个数有限
表中元素具有逻辑上的顺序性,在序列中各元素排序由先后次序
表中数据元素类型相同
表中数据具有抽象性
线性表是一种逻辑结构,表示元素的一对一的相邻关系。顺序表和链表是指存储结构。
3.线性表的相关操作:
InitList(&L):初始化表。构造一个空的线性表
Length(L):求表长
GetElem(L,i,&e):用e返回L中的第i个数据元素的值
LocateElem(L,e,compare( )):按值查找操作。在表L中查找具有给定关键字值得元素与e相同的元素的位序。
ListInsert(&L,i,e):在L中第i个元素之前插入元素e,表长加1。
ListDelete(&L,i,&e):删除L中第i个数据元素,并用e返回其值,L的长度减1。
4.线性表的存储类型(顺序存储、动态存储)
顺序存储
#define MaxSize 50
typedef struct{
ElemType data[ MaxSize];
int length;
}SqList;
动态存储
#define InitSize 100
typedef struct {
ElemType *data;
int MaxSize,length;
}SqList;
动态分配的语句(C && C++)
C语言: L.data=( Elem Type*)malloc(sizeof( Elem Type)*InitSize);
C++: L.data=new Elem Type[ InitSize];
顺序表的特点:
随机访问、存储密度高、逻辑上相邻物理上元素相邻、插入删除操作需要移动大量元素。
5.顺序表操作
插入操作:采用的思路是首先先判断i的输入是否合法,如果不合法,则返回false,表示插入失败。否则,将第i个元素及之后的元素进行后移一位,插入需要插入的元素e,顺序表长加1,插入成功返回true。
bool ListInsert(SqList &L,int i,ElemType e) {
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 ture;
}
采用动态存储结构进行插入操作:
思路:对要进行插入的操作进行判断,判断插入的i值是否合法,如果不合法,则返回error。对存储空间进行判断,如果满了的话,则增加分配。如果分配失败,则返回溢出。建立新的地址,增加存储容量。同时找到插入的位置,对其后的元素进行后移。
status ListInsert_Sq(SqList &L,int i,ElemType e) {
if(i<1|| i<L.length +1) return error;
//对于存储容量不够进行动态分配。如果失败,建立新的基址,增加容量。
if(L.length>=L.listsize) {
newbase= ( Elem Type*)realloc(L.elem,L.listsize+LISTINCREMENT)*sizeof( Elem Type);
if(!newbase) exit (overflow)
L.elem=newbase;
L.listsize+= LISTINCREMENT;
}
//找到插入的位置q,同时求线性表长度p,对于大于q的元素向后移一位。同时表长加1。
q=&(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;- -p) *(p+1)=*p;
*q=e;
++L.length;
return ok;
} //List Insert_Sq
删除操作:
思路:删除顺序表中第i个元素,成功时返回ture,并将删除元素用变量e返回,否则返回false。先判断i值是否合法,然后对删除的前部分,位序不变,而后面的元素进行地址减1,同时线性表的长度减1。
bool ListDelete(SqList &L,int i,int &e) {
if(i<1||i>L.length)
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 ture;
}
或者:
status ListDelete_Sq(SqList &L,int i, ElemType &e)
if(i<1|| i<L.length +1) return error;
p=&(L.elem[i-1]);
e=*p;
q=L.elem+L.length-1; //注意:由于地址取数组的元素时是第一个地址,所以这样写是正确的
for (++p;p<=q;++p) *(p-1)=*p;
- - L.length;
return ok;
}// ListDelete_Sq
按值查找:
在线性表中查找与e相同的元素,并返回其位序。
int LocateElem(SqList L,ElemType e) {
int i;
for (i=0;i<L.length;i++)
if(L.data[i]==e)
return i+1;
return 0;
}
逆转顺序表中的所有元素:
思路:采用第一个元素与最后一个元素进行对调的方法,第二个与倒数第二个对调,.......。
void Reverse(int A[ ], int n)
{
int i,t;
for (i=0;i<n/2;i++)
{
t=A[ i ];
A[ i ]=A[n-i-1];
A[n-i-1]=t;
}
}
当然还可以进行奇偶个数的分析,进行反转,可采用符号语句来进行。
void Reverse (int A[ ], int n)
{ void Reverse (int A[ ], int n)
int i,t,p;
p=(n%2==0)? n/2:n/2+1;
{
t=a[i];
a[i]=a[p+i];
a[p+i]=t;
}
}
进一步思考:如果不是全部逆转,而是部分逆转,比如:abcdef——> cdefab,此时上面的程序就不能解决问题了。
此时需要进行三步反转法:
即:将ab当做X
cdef当做Y
=> 即问题转化为:将XY——>YX的转化。
void ReverseString(char *s,int begin,int end)
{
while(begin<end)
{
char t=s[begin];
s[begin++]=s[end];
s[end- -]=t;
}
}
void LeftRoateString(char *s,int n,int m)
{
m%=n;
ReverseString(s,0,m-1);
ReverseString(s,m,n-1);
ReverseString(s,0,n-1);
}
思路就是三步反转法,时间复杂度是O(n)
6.线性表的链式表示
线性表的链式存储又称为单链表(非随机存储结构),它是通过一组任意的存储单元来存储线性表中的数据元素。
单链表中的结点类型的描述如下:
typedef struct LNode{ //定义单链表的结点类型
ElemType data; //数据域
struct LNode *next; // 指针域
}LNode ,*LinkList;
采用头插法建立单链表:
从一个空表开始,生成新的结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即结点之后。
LinkList CreateList1(LinkList &L) {
LNode *s;int x;
L=(LinkList)malloc(sizeof( LNode));
L—>next=NULL;
scanf("%d",&x);
while(x!=9999){
s=( LNode*)malloc(sizeof( LNode));
s—>data=x;
s—>next=L —>next;
L—>next=s;
scanf("%d",&x);
}
return L;
}
采用尾插法建立单链表:
将新结点插入到当前链表的表尾上,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点。
LinkList createList2( LinkList &L) {
int x;
L=(LinkList)malloc(sizeof( LNode));
LNode *s,*r=L;
scanf("%d ",&x);
while(x!=9999) {
s= ( LNode*)malloc(sizeof( LNode));
s—>data=x;
r—>next=s;
r=s;
scanf("%d",&x);
}
r—>next=NULL;
return L;
}
按序号查找结点值:
LNode *GetElem( LinkList L,int i)
int j=1;
LNode *p=L—>next;
if(i==0)
return L;
if(i<1)
return NULL;
while(p&&j<i) {
p=p—>next;
j++;
}
return p;
}
按值查找表结点:
LNode *GetElem( LinkList L, ElemType e)
{
ElemType *p=L—>next;
while(p!=NULL&&p—>data!=e)
p=p —>next;
return p;
}
插入操作:
p=GetElem(L,i-1);
s—>next=p—>next;
p—>next=s;
删除操作:
p=GetElem(L,i-1);
q=p—>next;
p
—>next=q—>next ;
free(q);
free(q);