線性表的定義和特點
定義:由N個數據特性相同的元素構成的有限序列稱為線性表
特點:除第一個元素之外 結構中每一個數據元素均只有一個前驅;除最后一個元素外結構中每一個元素只有一個后繼。
線性表的順序存儲表示和實現
順序表定義:線性表的順序表示指的是用一組地址連續的存儲單元依次存儲線性表的數據元素稱這種存儲結構的線性表為順序表
特點:邏輯上相鄰的數據元素 物理次序上也是相鄰的,線性表的順序存儲結構是一種隨機存取的存儲結構
計算
線性表的每一個元素占用t個存儲單元 則第i+1個數據元素的存儲位置和第i個數據元素的存儲位置之間滿足關系:loc(a_{i+1})=loc(a_{i})+t
所有數據元素存儲位置之間滿足關系:loc(a_{i})=loc(a_{1})+(i-1)*t
基本操作
順序表的初始化: `Status initlist(sqlist &L)
{///構造一個空的順序表L
L.elem=new ElemType[MAXSIZE]; //為順序表分配一個大小為maxsize的數組空間
if(!L.elem) exit (overflow); //存儲分配失敗退出
L.length=0; //空表的長度為0
return ok;}`
順序表的存儲結構:`#define MAXsize 100 ///順序表可能達到的最大長度
typedef struct
{
ElemType *elem; //存儲空間的基地址
int length; //當前長度
}Sqlist; ///順序表的結構類型為Sqlist
` 線性表存儲到存儲單元中 邏輯位序與物理位序相差1;
順序表的取值:`Status GetElem(Sqlist L,int i,ElemType &e)
{
if(i>1||i>length) return error; //判斷i值是否合理
e=L.elem[i-1]; ///elem[i-1]單元存儲第i個數據元素
return ok;
} `
順序表的查找:`int LocateElem(Sqlist L,ElemType e)
{ //在順序表L中查找值為e的數據元素 返回其序號
for(i=0;i<l.length;i++)
if(L.elem[i]==e)
return i+1; //查找成功返回序號i+1
return 0; ///查找失敗返回 0
}`
順序表的插入:`Status Listinsert (Sqlist &L,int i,ElemType e)
{ //在順序表L中第i個位置插入新的元素e,i值的合法范圍是1<=i<=L.length+1
if((i<1)||(i>L.length+1)) return error; //i值不合法
if(L.length==MAXSIZE) return error; ///當前存儲值已經滿
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j]; //插入位置及之后的元素后移
L.elem[i-1]=e; //將新元素e放入第i個位置
++L.length; //表長加1
return ok;
}`
順序表的刪除:`Status ListDelete(Sqlist &L,int i)
{ //在順序表L中刪除第i個元素 i值的合法范圍是1<=i<=L.length
if((i<1)||(i>L.length)) return error; //i值不合法
for(j=i;j<=length-1;j++)
L.elem[j-1]=L.elem[j]; //被刪除元素之后的元素前移
--L.length; //表長減1
return ok;
}`
順序表的時間復雜度:查找 插入 刪除的算法平均時間復雜度為O(n) 空間復雜度為O(1)
順序表的優點:存儲密度大 可以隨機存取表中任一元素
缺點:插入刪除元素需要移動大量元素 浪費存儲空間 屬於靜態存儲形式,數據元素個數不能自由擴充
線性表的鏈式存儲表示和實現
線性表的鏈式存儲結構的特點:用一組任意的存儲單元存儲線性表的數據元素(存儲單元可以連續也可以不連續)
結點有兩個域:其中存儲數據元素信息的域稱為數據域;存儲直接后繼存儲位置的域稱為指針域 指針域中存儲的信息稱為指針或者鏈
鏈表分類:單鏈表 循環鏈表 雙向鏈表 二叉鏈表 十字鏈表 鄰接表 鄰接多重表
空表:當沒有頭結點的時候 頭指針為空表示空表,有頭結點時 頭結點的指針域為空時表示空表
鏈表增加頭結點的作用:
- 便於首元結點的處理(首元結點的地址存儲在頭結點的指針域中)
- 便於空表和非空表的統一處理
單鏈表是非隨機存取的存儲結構 取得第i個數據元素必須從頭指針出發順鏈尋找 也稱為順序存取的存取結構。
單鏈表的插入:一般情況 在第i 個元素插入一個元素時 需要從最后一個元素即第n個元素開始依次向后移動一個元素 直至第i個元素(一共需要移動n-i+1個元素)
基本操作:
單鏈表的初始化:`Status InitList(LinkList &L)
{ ///構造一個空的單鏈表L
L=new LNode; //生成新結點作為頭結點 用頭指針L指向頭結點
L->next=NULL; //頭結點的指針域置空
return ok;
}`
單鏈表的取值:`Status GetElem(LinkList L,int i,ElemType &e)
{//在頭結點的單鏈表L中根據序號i獲取元素的值 用e返回L中第i個數據的值
p=L->next;j=1; //初始化 p指向首元結點 計數器j初值賦值為1
while(p&&j<i) ///順鏈域向后掃描 直到p為空或者p指向第i個元素
{
p=p->next; //p指向下一個結點
++j;
}
if(!p||j>i) return error; //i值不合法i>n或者i<=0
e=p->data; //取第i個結點的數據域
return ok;}`
單鏈表的按值查找:`LNode *LocateElem(LinkList L,ElemType e)
{//在帶頭結點的單鏈表L中查找值為e的元素
p=L->next; //初始化 p指向首元結點
while(p&& p->data!=e) //順鏈域向后掃描 直到p為空或p所指的結點的數據域等於e
p=p->next; //p指向下一個結點
return p; ///查找成功返回值為e的結點地址p 查找失敗p為NULL
}`
單鏈表的插入:`Status ListInsert(ListList &L,int i,ElemType e)
{ //在帶頭結點的單鏈表L中第i個位置插入值為e的新節點
p=L;j=0;
while(p &&(j<i-1))
{p=p->next; ++j;} //查找第i-1個結點 p指向該結點
if(!p||j>i-1) return error; //i>n+1或者i<1
s=new LNode; //生成新節點*s
s->data=e; //將結點*s的數據域置為e
s-next=p->next; //將結點*s的指針域指向結點ai
p->next=s; //將結點*p的指針域指向結點*s
return ok;
}`
單鏈表的刪除:`Status ListDelete(Linklist &L,int i)
{ ///在帶頭結點的單鏈表L中 刪除第i個元素
p=L;j=0;
while((p->next)&& (j<i-1)) //查找第i-1個結點 P指向該結點
{p=p->next;++j; }
if(!(p-next)||(j>i-1)) return error; //當i>n或i<1時刪除位置不合理
q=p->next; //臨時保存被刪除結點的地址以備釋放
p-next=q->next; //改變刪除結點前驅結點的指針域
delete q; //釋放刪除結點的空間
return ok;
}`
前插法創建單鏈表:`void CreateList_H(LinkList &L,int n)
{ //逆位序輸入n個元素的值 建立帶表頭結點的單鏈表L
L=new LNode;
L->next=NULL; //先建立一個帶頭結點的空鏈表
for(i=0;i<n;++i)
{
p=new LNode; //生成新節點*p
cin>>p-data; //輸入元素值賦給新節點*p的數據域
p->next=L-next; //將L-NEXT后面的數據元素賦給新節點p的后繼
L-next=p; //將新節點p插入到頭結點之后
}`
后插法創建單鏈表:`void CreateList_H(LinkList &L,int n)
{ //正位序輸入n個元素的值 建立帶表頭結點的單鏈表L
L=new LNode;
L->next=NULL; //先建立一個帶頭結點的空鏈表
r=L; //尾指針r指向頭結點
for(i=0;i<n;++i)
{
p=new LNode; //生成新節點*p
cin>>p-data; //輸入元素值賦給新節點*p的數據域
p->next=NULL;
r->next=p; //將新節點*p插入尾結點*r之后
r=p; //r指向新的尾結點*p
}`
雙向鏈表:雙向鏈表的結點中有兩個指針域 一個指向后繼一個指向前驅
雙向鏈表的存儲結構:`typedef struct DuLNode
{
ElemType data; //數據域
struct DULNode *prior; //指向直接前驅
struct DULNode *next; //指向直接后繼
}DuLNode, *DuLinkList;`
有序表:若有序表的數據元素相互之間可以比較 並且數據元素在線性表中依值非遞減或非遞增有序排列 則稱該線性表為有序表
算法設計:
1.有序表的合並問題: 將兩個遞增的有序鏈表合並為一個遞增的有序鏈表 要求結果鏈表仍使用原來兩個鏈表的存儲空間,不占用另外的存儲空間。表中不允許有重復的結構
`void MergeList(LinkList &La,LinkList &Lb,LinkList &Lc) //合並鏈表La和Lb,合並后新表使用頭部指針Lc指向
{
pa=LA->next; pb=LB->next; //pa和pb分別是鏈表LA和LB的工作指針 初始化為相應鏈表的第一個結點
LC=pc=La; ///用La的頭結點作為Lc的頭結點
while(pa&&pb)
{
if(pa->data<pb->data) //取較小者La元素
{ pc->next=pa; //將pa鏈接到pc的后面
pc=pa; //pc指向pa
pa=pa->next; ///pa的指針后移
}
else if(pa->data>pb->data) {
pc->next=pb; //將pb鏈接到pc的后面
pc=pa; //pc指向pb
pa=pa->next; ///pb的指針后移
}
else ///相等時取La中的元素 刪除Lb中的元素
{
pc->next=pa; //將pa鏈接到pc的后面
pc=pa; //pc指向pa
pa=pa->next; ///pa的指針后移
q=pb->next; //保存pb的地址在q中
delete pb; //釋放pb的存儲空間
pb=q ///將pb指針后移
}
}
pc->next=pa? pa :pb; ///當一個表為空時 則插入剩余表段
delete Lb; ///釋放Lb的頭結點
} `
2.設計算法 通過一趟遍歷確定長度為n的單鏈表中值最大的結點
`ElemType Max(LinkList &L ,int n)
{ if(L-next==Null) return NUll; ///判斷是不是空表
pmax=L->next; //假定第一個結點的數據具有最大值
p=L-next-next; //下一個結點
while(p!=Null) { //如果下一個結點存在的話
if(p->data>pmax->data)
pmax=p; //如果p的值大於Pmax的值,則重新賦值
p=p->next; ///遍歷鏈表
}
return pmax-data;}`
3.雙向循環鏈表問題:已知p指向雙向循環鏈表中的一個結點 其結點結構為data prior next 三個域 寫出算法change(p) 交換p所指向的結點及其前驅結點的順序
t | q | p | 交換前 |
---|---|---|---|
t | p | q | 交換后 |
`void Exchange (LinkedList p) // p是雙向循環鏈表的一個結點 算法將p所指結點與其前驅結點交換
{ q=p->prior; //q為P的前驅
q->prior->next=p; //q的前驅的后繼為p 交換后(t的后繼更改)
p-prior=q->prior; //交換后p的前驅為交換前 q的前驅
q->next=p->next; ///交換后q的后繼是交換前p的后繼
q-prior=p; ///交換后p為q 的前驅
p->next-prior=q; ///前p的后繼現在的前驅為q
p->next=q; //現在p的后繼是q
} ` //結束