Table of Contents
定義
數據結構是相互之間存在一種或多種特定關係的數據元素的集合。根據數據元素之間關係的不同特性,通常有如下4類基本結構:
- 集合:結構中的數據元素之間除了“同屬於一個集合”的關係外,別無其他的關係。如:廣義表。
- 線性結構:結構中的數據元素之間存在一個對一個的關係。如:鏈表。
- 樹形結構:結構中的數據元素之間存在一個對多個的關係。如:二叉樹。
- 圖(網)狀結構:結構中的數據元素之間存在多個對多個的關係。
在線性結構中,根據存儲方式分爲順序表、鏈表,根據對錶的操作限制,分爲棧和隊列。
順序表的特徵是,在內存中佔用連續的存儲單元,可以簡單的理解爲順序表就是數組。只是根據需要,在實際應用中動態分配順序表佔用的內存單元。而數組是在編譯的時候,預分配了指定大小的內存單元,因此如下代碼段會在編譯的時候報錯。
int len = 10;
char arr[len];
但是順序表又會有數據全部的特點:可以根據下標直接訪問、不方便插入和刪除元素(因爲需要移動後續的元素)。
實現
定義結構
typedef int SeqType; //存儲單元類型
typedef struct{
SeqType *elem; //存儲空間基地址
int length; //當前長度
int listsize; //當前分配的存儲容量(以sizeof(ElemType)爲單位)
} SqList;
結構體內,有三個元素:存儲空間基地址,類似於數組首地址;當前長度,記錄順序表中有效存儲單元個數;當前分配的存儲容量,順序表中,最多容納的存儲單元個數。當順序表中所有存儲單元已經被使用,在下次插入元素之前,需要新增存儲單元。這點是數組所不具有的特性。
*注:定義一個存儲單元類型SeqType是爲了使順序表適和更多數據類型,使用的時候修改SeqType類型即可。
定義操作
創建順序表
/**
* 創建順序表
*/
SqList createList_sq() {
//SqList list;
//return list;
SqList* list = (SqList*)malloc(sizeof(SqList));
return *list;
}
這裏提供兩種創建順序表的代碼,一種是由系統分配list佔用的內存,一種是自己動態分配的內存,需要在程序運行之前手動釋放佔用的內存空間。
初始化順序表
/**
* 初始化順序表
* 返回1 表示初始化成功
* 返回0 表示初始化失敗
*/
int initList_sq(SqList &L) { //只有在C++中才會有引用的存在
L.elem = (SeqType *) malloc(sizeof(SeqType) * LIST_INIT_SIZE);
if (!L.elem)
return 0; //內存分配失敗,存儲空間不夠
L.length = 0; //表示順序表爲空
L.listsize = LIST_INIT_SIZE; //表示順序表裏,最大存儲單元個數
return 1;
}
分配順序表的存儲單元,初始化順序表屬性的值。
插入元素
/**
* 插入順序表
* 下標是負數就插入到結尾
*/
int insertList_sq(SqList &L, int index, SeqType val) {
if (index > L.length) { //存儲的下表超出順序表實際的長度
printf("插入的下標超出順序表的實際長度");
return 0;
}
if (index < 0) //下標是負數,插入到結尾
index = L.length;
if (L.length == L.listsize) { //順序表的存儲單元已經存滿
printf("順序表的存儲單元已滿,繼續分配新的存儲單元。");
SeqType* newBase = (SeqType*) realloc(L.elem,
(L.listsize + LISTINCREMENT) * sizeof(SeqType)); //繼續分配存儲單元
if (!newBase) {
printf("分配內存單元失敗");
return 0;
}
L.elem = newBase;
L.listsize += LISTINCREMENT;
}
//尋找合適的插入位置,index後面的元素向後移動
for (int i = L.length; i > index; i--) {
L.elem[i] = L.elem[i - 1]; //向後移動
}
L.elem[index] = val; //插入元素
L.length++;
return 1;
}
將元素插入到指定的位置。插入之前,需要先判斷順序表中是否已經存滿,再根據需要新增存儲單元,最後插入元素。
/**
* 插入順序表(結尾的位置)
* 與上面的函數是重名函數,這叫函數重載,在C++裏面支持
*/
int insertList_sq(SqList &L, SeqType val) {
return insertList_sq(L, L.length, val);
}
*引用和重載,是C++中才支持,因此需要在cpp文件中編譯。
刪除元素
/**
* 刪除指定的元素
* 返回0 找不到指定的元素,刪除失敗。
* 返回1 找到待刪除的元素,刪除成功。
*/
int removeList_sq(SqList &L, SeqType val) {
int index = -1; //記錄匹配到的下標
for (int i = 0; i < L.length; i++) {
if (L.elem[i] == val) {
//找到匹配的val,結束循環
index = i;
break;
}
}
if (index < 0)
return 0;
for (; index < L.length - 1; index++) {
L.elem[index] = L.elem[index + 1];
}
L.length--;
return 1;
}
刪除指定元素,需要先找到下標。依次移動下標後面的結點,修改length值。
/**
* 根據下標刪除是指定的結點,並返回元素的值
* 返回0 下標超出順序表長度,刪除失敗。
* 返回1 下標正確,刪除元素,並且將已刪除元素值轉給elem
*/
int removeList_sq(SqList &L, int index, SeqType &elem) {
if (index >= L.length) //下標超出順序表的長度
return 0;
index = index < 0 ? L.length : index; //下標負數表示刪除最後一個節點
elem = L.elem[index];
for (int i = index; i < L.length - 1; i++) {
L.elem[i] = L.elem[i + 1];
}
L.length--;
return 1;
}
先取到指定下標的元素,賦值給elem,然後依次移動下標後面的結點。最後修改length值。
銷燬順序表
/**
* 銷燬順序表
*/
void destoryList_sq(SqList &L) {
free(L.elem); //釋放存儲空間
L.length = 0;
L.listsize = 0;
// free(&L);
}
重點釋放順序表的存儲單元。如果順序表自身的內存也是動態分配的,需要手動釋放。
最後附上,頭文件的定義。
/*
* sqlist.h
*
* 線性表的順序存儲
* Created on: 2016年8月30日
* Author: flueky
*/
#ifndef SQLIST_H_
#define SQLIST_H_
#define LIST_INIT_SIZE 50
#define LISTINCREMENT 10
typedef int SeqType; //存儲單元類型
typedef struct{
SeqType *elem; //存儲空間基地址
int length; //當前長度
int listsize; //當前分配的存儲容量(以sizeof(ElemType)爲單位)
} SqList;
/**
* 創建順序表
*/
SqList createList_sq();
/**
* 初始化順序表
*/
int initList_sq(SqList &);
/**
* 插入順序表
*/
int insertList_sq(SqList &,int index,SeqType);
/**
* 插入順序表(結尾的位置)
*/
int insertList_sq(SqList &,SeqType);
/**
* 在順序表中移除指定位置元素,下標從0開始
*/
int removeList_sq(SqList &,int,SeqType &);
/**
* 在順序表中刪除指定元素
*/
int removeList_sq(SqList &,SeqType);
/**
* 銷燬順序表
*/
void destoryList_sq(SqList &);
#endif /* SQLIST_H_ */
摘自嚴蔚敏版《數據結構》