1. 線性表綜述:
線性表是具有相同特性數據元素的一個有限序列
該序列中所包含元素的個數叫做線性表的長度
當一個線性表有序時,成爲有序表,屬於邏輯結構
線性表的邏輯特徵:
對於至少含有一個元素的線性表來說
除起始元素沒有前驅元素外,其他元素都有一個唯一前驅
除終端元素沒有後繼元素外,其他元素都有一個唯一後繼
在線性表中,每個元素最多隻有一個前驅和後繼元素
線性表存儲結構:
線性表有順序和鏈式兩類存儲結構,前者成爲順序表,後者成爲鏈表
順序表特點:
無需爲元素間的邏輯關係兒增加額外的存儲空間
可以隨機存取表中的任何一個元素,但是要佔用連續的空間
而且插入和刪除元素時需要移動大量的元素,平均需要移動n/2個元素
鏈表特點:
不僅存儲每個節點的信息,而且也存儲節點之間的關係(通過指針)
鏈表的地址可以是不連續的,不支持隨機訪問,插入和刪除比較快
鏈表中的第一個節點的存儲位置通常被成爲頭指針
如果鏈表有頭結點,頭指針爲頭結點的地址
如果沒有頭結點,頭指針爲開始節點的地址
通常用頭指針來標識一個鏈表
1、順序表程序:
//線性表存儲空間的初始分配量
#define LIST_INIT_SIZE 10
//順序存儲結構的分配增量
#define LIST_INCREMENT 5
typedef int ElemType;
//順序存儲結構體
struct SqList
{
ElemType* elem; //存儲空間基址
int length; //數據元素當前長度
int listsize; //當前分配的總容量(元素個數)
};
/*******基礎操作********/
//初始化順序表
void InitList(SqList* L)
{
L->elem = (ElemType*)malloc(sizeof(ElemType)*LIST_INIT_SIZE);
if (L->elem == NULL)
{
printf("空間申請失敗!\n");
exit(-1);
}
L->length = 0;
L->listsize = LIST_INIT_SIZE;
}
//銷燬順序表
void DestroyList(SqList* L)
{
free(L->elem);
L->elem = NULL;
L->length = 0;
L->listsize = 0;
}
//清空順序表
void ClearList(SqList* L)
{
L->length = 0;
}
//判空
int EmptyList(SqList* L)
{
if (L->length == 0)
return 1;
return 0;
}
//順序表長
int ListLength(SqList* L)
{
return L->length;
}
//得到順序表第index元素
int GetElem(SqList* L, int index, ElemType* e)
{
//要找的元素不在順序表範圍內
if (index<1 || index>L->length)
return -1;
*e = L->elem[index-1];
return 1;
}
//返回元素的位置
int LocateElem(SqList* L, ElemType* e)
{
for (int i=0; i<L->length; ++i)
{
//元素相等
if (L->elem[i] == *e)
return i;
}
return -1;
}
//返回元素的前驅
int PriorElem(SqList* L, ElemType* e)
{
int index = LocateElem(L, e);
//第一個元素沒有前驅
if (index == -1 || index == 0)
return -1;
return index-1;
}
//返回元素的後繼
int NextElem(SqList* L, ElemType* e)
{
int index = LocateElem(L, e);
//最後一個元素沒有後繼
if (index == -1 || index == L->length-1)
return -1;
return index+1;
}
//順序表插入
int ListInsert(SqList* L, int index, ElemType* e)
{
//判斷插入位置是否合法
if (index<1 || index>L->listsize)
return -1;
//判斷空間是否滿
if (L->length == L->listsize)
{
L->elem = (ElemType*)realloc(L->elem, (L->listsize+LIST_INCREMENT)*sizeof(ElemType));
if (L->elem == NULL)
exit(-1);
L->listsize += LIST_INCREMENT;
}
//開始插入
for (int i=L->length-1; i>=index-1; --i)
{
//循環後移
L->elem[i+1] = L->elem[i];
}
//插入
L->elem[index-1] = *e;
//長度++
L->length++;
return 1;
}
//順序表刪除
int ListDelete(SqList* L, int index)
{
//判斷刪除位置是否合法
if (index<1 || index>L->length)
return -1;
//開始刪除
for (int i=index-1; i<L->length; ++i)
{
L->elem[i] = L->elem[i+1];
}
--L->length;
return 1;
}
//順序表遍歷
void ListTraverse(SqList* L)
{
for (int i=0; i<L->length; ++i)
{
cout << L->elem[i] << ' ';
}
cout << endl;
}
/*******進階操作********/
//合併兩個順序表
//將所有在順序表Lb中,但不在La中的元素插入到La中
void Union(SqList* La, SqList* Lb)
{
int lena = ListLength(La);
int lenb = ListLength(Lb);
for (int i=0; i<Lb->length; ++i)
{
//在La中沒有出現就插入
if (LocateElem(La, &(Lb->elem[i])) == -1)
ListInsert(La, La->length+1, &(Lb->elem[i]));
}
}
//歸併兩個有序順序表
void MergeList(SqList* La, SqList* Lb, SqList* Lc)
{
int i=0, j=0;
int lena = La->length;
int lenb = Lb->length;
//初始化順序表Lc
InitList(Lc);
//兩個都沒有完的時候
while (i<lena & j<lenb)
{
//La<Lb
if (La->elem[i] < Lb->elem[j])
{
//尾插La裏面的元素
ListInsert(Lc, Lc->length+1, &(La->elem[i]));
++i;
}
else //Lb<La
{
//尾插Lb裏面的元素
ListInsert(Lc, Lc->length+1, &(Lb->elem[j]));
++j;
}
}
//其中有一個插完了
while (i < lena)
{
//尾插La裏面的元素
ListInsert(Lc, Lc->length+1, &(La->elem[i]));
++i;
}
while (j < lenb)
{
//尾插Lb裏面的元素
ListInsert(Lc, Lc->length+1, &(Lb->elem[j]));
++j;
}
}
int main()
{
/*基礎操作測試
SqList L;
//初始化
InitList(&L);
cout << "初始化後: 首地址:" << L.elem << "元素個數:" << L.length << "總容量:" << L.listsize << endl;
//頭插
for (int i=1; i<=5; ++i)
ListInsert(&L, 1, &i);
cout << "頭插5次:" << endl;
ListTraverse(&L);
//是否爲空
cout << "順序表是否爲空:" << EmptyList(&L) << endl;
//大小
cout << "順序表大小:" << ListLength(&L) << endl;
//清空順序表
cout << "清空順序表:" << endl;
ClearList(&L);
cout << "清空後大小:" << ListLength(&L) << endl;
//尾插
for (int i=6; i<=10; ++i)
ListInsert(&L, L.length+1, &i);
cout << "尾插5次:" << endl;
ListTraverse(&L);
//刪除第3個元素
cout << "刪除第3個元素:" << endl;
ListDelete(&L, 3);
cout << "刪除後:" << endl;
ListTraverse(&L);
//銷燬順序表
DestroyList(&L);
cout << "首地址:" << L.elem << "元素個數:" << L.length << "總容量:" << L.listsize << endl;
cout << "順序表已成功銷燬" << endl;
*/
SqList La, Lb, Lc;
//初始化
InitList(&La);
InitList(&Lb);
InitList(&Lc);
//插入La
for (int i=1; i<=5; ++i)
ListInsert(&La, La.length+1, &i);
//插入Lb
for (int i=2; i<=10; i+=2)
ListInsert(&Lb, Lb.length+1, &i);
//合併兩個順序表
//Union(&La, &Lb);
//遍歷順序表La
//cout << "合併後爲:" << endl;
//歸併La和Lb
MergeList(&La, &Lb, &Lc);
cout << "La和Lb歸併後:" << endl;
ListTraverse(&Lc);
return 0;
}
2.鏈表:
和順序表相比,鏈表在實現插入、刪除操作時,不需要移動大量的元素
但是不容易實現隨機存取,適用於經常進行插入和刪除的線性表
一般用一個頭指針來唯一的標識一個鏈表
鏈表的插入和刪除:
插入和刪除要先把後邊的鏈表鏈起來,然後再鏈接上前面的鏈表,都要找到當前節點和前驅
鏈表的插入和刪除主要考慮:第一個元素、中間元素、最後一個元素
其中,中間元素和最後一個元素的操作都相同,只剩下第一個元素和其餘元素的區別
會因爲帶頭結點和不帶頭結點而有所區別:比較重要:
不帶頭結點:
因爲如果不帶頭結點插入表頭或者刪除表頭的話,得修改鏈表的唯一標識指針即 L
帶頭結點的話,令頭結點爲第0個元素,表頭結點爲第1個元素
就可以不用修改L來插入或者刪除表頭了,這時修改的是頭結點的指針域
鏈表指針始終指向的是頭結點。所以就不用考慮插入是在表頭還是在其他地方了
帶頭結點:
帶頭結點的鏈表的插入和刪除,都是定義兩個結點指針變量 p=L, q=NULL
p指向要插入或刪除的結點,q指向p的前驅結點
因爲不管是插入和刪除,都要找到待操作結點和該結點的前驅
插入和刪除:
插入:位置判斷條件是:pos<1 || NULL==q 就退出
因爲插入時,可以插在1–length+1之間,即 p==NULL是允許的
刪除:位置判斷條件是:pos<1 || NULL==p 就退出
因爲在刪除時,可以刪除1–length之間,要刪除的結點必須不能爲空,即 p!=NULL
typedef int ElemType;
//鏈表結構體定義
typedef struct Node
{
ElemType data; //數據域
struct Node* next; //指針域
}Node, *LinkList;
//初始化
//也可以用Node** L來代替LinkList* L
void InitList(LinkList* L)
{
//產生頭結點,並使L指向該頭結點
*L = (Node*)malloc(sizeof(Node));
if (*L == NULL)
{
cout << "分配失敗!" << endl;
exit(-1);
}
(*L)->next = NULL;
}
//銷燬鏈表
//得傳入一個鏈表的指針
void DestroyList(LinkList* L)
{
Node* q = NULL;
while (NULL != *L)
{
q = (*L)->next;
free(*L);
*L = q;
}
cout << "銷燬成功!" << endl;
}
//清空一個鏈表,不改變頭結點
void ClearList(LinkList L)
{
Node* p = NULL;
Node* q = NULL;
p = L->next; //p指向第一個結點
while (NULL != p) //循環刪除鏈表結點
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;
}
//鏈表判空
int ListEmpty(LinkList L)
{
if (L->next == NULL)
return 1;
return 0;
}
//統計鏈表長度
int ListLength(LinkList L)
{
Node* q = L->next;
int length = 0;
while (NULL != q)
{
++length;
q = q->next;
}
return length;
}
//取得鏈表第i個元素
int GetElem(LinkList L, int pos, ElemType* e)
{
Node* p = L->next;
int count = 1;
while (NULL != p && count < pos)
{
++count;
p = p->next;
}
//沒有找到
if (NULL==p || count>pos)
return 0;
//找到
*e = p->data;
return 1;
}
//獲取元素在鏈表中的位置
int LocateElem(LinkList L, ElemType* e)
{
Node* p = L->next; //使p指向第一個結點
int pos = 1;
while (NULL!=p && p->data!=(*e))
{
++pos;
p = p->next;
}
if (NULL == p)
return -1;
return pos;
}
//找元素的前驅
int PriorElem(LinkList L, ElemType* cur_e, ElemType* pre_e)
{
Node* p = L->next; //指向當前結點
Node* q = NULL; //指向當前結點的前驅
while (NULL!= p && p->data!=(*cur_e))
{
q = p;
p = p->next;
}
if (NULL == p)
return -1;
*pre_e = q->data;
return 1;
}
//找到元素的後繼
int NextElem(LinkList L, ElemType* cur_e, ElemType* next_e)
{
Node* p = L->next;
while (p->next != NULL && p->data!=(*next_e))
{
p = p->next;
}
if (NULL == p->next) //沒有找到
return -1;
*next_e = p->next->data;
return 1;
}
//在指定位置插入
//可以在1和length+1位置之間進行插入
int ListInsert(LinkList L, int pos, ElemType* e)
{
Node* p = L; //指向要插入的位置
Node* q = NULL; //指向要插入的前驅
int count = 0;
//尋找要插入結點的前驅q
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
//插入位置不對 大於表長NULL==p或者小於1
if (pos<1 || NULL==q)
{
cout << "插入位置不正確!" << endl;
return -1;
}
p = (Node*)malloc(sizeof(Node));
p->data = *e;
p->next = q->next;
q->next = p;
return 1;
}
//在指定位置刪除元素
//在1和length之間可以刪除
int ListDelete(LinkList L, int pos, ElemType* e)
{
Node* p = L; //指向要刪除的位置
Node* q = NULL; //指向要刪除位置的前驅
int count = 0;
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
if (pos<1 || NULL==p) //位置不在範圍內
{
cout << "刪除位置不正確!" << endl;
return -1;
}
//刪除結點
q->next = p->next;
*e = p->data;
free(p);
p = NULL;
return 1;
}
/***********不帶頭結點的鏈表的插入和刪除***********/
int ListInsert(LinkList* L, int pos, ElemType* e)
{
int count = 1;
Node* p = NULL;
Node* q = NULL;
p = (Node*)malloc(sizeof(Node));
p->data = *e;
p->next = NULL;
//表頭
if (pos == 1)
{
p->next = (*L);
*L = p;
}
else //其他位置
{
while (NULL!=q && count<pos-1)
{
q = q->next;
++count;
}
//插入位置大於表長
if (pos<1 || NULL == q)
{
cout << "插入位置不對!" << endl;
return -1;
}
p->next = q->next;
q->next = p;
}
return 1;
}
int ListDelete(LinkList* L, int pos, ElemType* e)
{
Node* p = *L;
Node* q = NULL;
int count = 1;
//頭刪
if (pos == 1)
{
p = (*L)->next;
*L = p->next;
free(p);
p = NULL;
}
else
{
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
if (pos<1 || NULL==p)
{
cout << "插入位置不合法!" << endl;
return -1;
}
q->next = p->next;
*e = p->data;
free(p);
p = NULL;
}
}
//鏈表遍歷
void ListTraverse(LinkList L)
{
Node* p = L->next;
while (NULL != p)
{
cout << p->data << endl;
p = p->next;
}
}
//頭插建立鏈表
void HeadCreateList(LinkList* L, int n)
{
//建立頭結點
*L = (Node*)malloc(sizeof(Node));
(*L)->next = NULL;
Node* p = NULL;
int num = 0;
for (int i=0; i<n; ++i)
{
p = (Node*)malloc(sizeof(Node));
cin >> num;
p->data = num;
p->next = (*L)->next;
(*L)->next = p;
}
}
//尾插法建立鏈表
void TailCreateList(LinkList* L, int n)
{
//建立頭結點
(*L) = (Node*)malloc(sizeof(Node));
(*L)->next = NULL;
Node* p = NULL;
Node* cur = *L;
int num = 0;
for (int i=0; i<n; ++i)
{
//找到尾結點
while (NULL != cur->next)
{
cur = cur->next;
}
//開闢新結點
p = (Node*)malloc(sizeof(Node));
cin >> num;
p->data = num;
p->next = NULL;
//插入尾部
cur->next = p;
}
}
//合併兩個鏈表(合併不同元素)
void Union(LinkList La, LinkList Lb)
{
ElemType e;
int len_a = ListLength(La);
int len_b = ListLength(Lb);
for (int i=1; i<=len_b; ++i)
{
GetElem(Lb, i, &e);
if (LocateElem(La, &e) == -1)
ListInsert(La, ++len_a, &e);
}
}
//歸併兩個有序鏈表
void MergeList(LinkList La, LinkList Lb, LinkList Lc)
{
int len_a = ListLength(La);
int len_b = ListLength(Lb);
int i=1, j=1;
Node* pa = La->next;
Node* pb = Lb->next;
Node* pc = NULL;
while (i<=len_a && j<=len_b)
{
//La < Lb
if (pa->data < pb->data)
{
//開闢新結點
pc = (Node*)malloc(sizeof(Node));
pc->data = pa->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//La後移一個
pa = pa->next;
++i;
}
else
{
//開闢新結點
pc = (Node*)malloc(sizeof(Node));
pc->data = pb->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//Lb後移一個
pb = pb->next;
++j;
}
}
while (i <= len_a)
{
//開闢新結點
pc = (Node*)malloc(sizeof(Node));
pc->data = pa->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//後移一個
pa = pa->next;
++i;
}
while (j <= len_b)
{
//開闢新結點
pc = (Node*)malloc(sizeof(Node));
pc->data = pb->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//後移一個
pb = pb->next;
++j;
}
}
int main()
{
/****基礎功能測試****/
/*
LinkList L;
//初始化鏈表
InitList(&L);
//頭插
for (int i=0; i<5; ++i)
ListInsert(L, 1, &i);
//在第最後一個位置插入
int num = 10;
ListInsert(L, 6, &num);
//遍歷
cout << "插入後的鏈表:" << endl;
ListTraverse(L);
//鏈表長度
cout << "鏈表長度爲:" << ListLength(L) << endl;
//在第1個位置刪除
cout << "刪除後的鏈表:" << endl;
ListDelete(L, 1, &num);
//遍歷
ListTraverse(L);
//清空鏈表
cout << "清空鏈表:" << endl;
ClearList(L);
//遍歷
ListTraverse(L);
//鏈表長度
cout << "鏈表長度爲:" << ListLength(L) << endl;
//銷燬鏈表
cout << "銷燬鏈表:" << endl;
DestroyList(&L);
*/
LinkList L;
//頭插創建鏈表
//HeadCreateList(&L, 3);
//尾插創建鏈表
TailCreateList(&L, 3);
//遍歷
ListTraverse(L);
/****進階功能測試****/
LinkList La, Lb, Lc;
//初始化鏈表
InitList(&La);
InitList(&Lb);
InitList(&Lc);
//頭插
for (int i=1; i<=5; ++i)
ListInsert(La, i, &i);
//尾插
int count = 1;
for (int i=2; i<=10; i+=2)
ListInsert(Lb, count++, &i);
//遍歷
ListTraverse(La);
ListTraverse(Lb);
//Union(La, Lb);
cout << "歸併後的鏈表爲:" << endl;
MergeList(La, Lb, Lc);
ListTraverse(Lc);
return 0;
}