順序表:
即線性表的順序存儲結(jié)構(gòu), 其邏輯結(jié)構(gòu)與物理存儲結(jié)構(gòu)一致.內(nèi)存分配連續(xù),需要考慮初始內(nèi)存分配和內(nèi)存追加的問題.
基礎(chǔ)算法:
1.類型定義&宏定義
#define C_TRUE 0
#define C_FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 // 線性表存儲空間的初始分配量
#define LISTINCREMENT 10 // 線性表存儲空間的分配增量
typedef struct
{
int *elem; // 首地址指針
int length; // 長度
int listsize; // 當前分配的存儲容量
}List;
2.創(chuàng)建空表
int ListInit(List *L) {
L->elem = (int *)malloc(LIST_INIT_SIZE *sizeof(int));
if (NULL == L->elem) exit(OVERFLOW);
L->length = 0;
L->listsize = LIST_INIT_SIZE;
return OK;
}
3.追加元素
int ListAdd(List *L, int e) {
if (L->length >= L->listsize) {
int *newbase = (int *)realloc(L->elem, (L->listsize + LISTINCREMENT) * sizeof(int));
if (!newbase) exit(OVERFLOW);
L->elem = newbase;
L->listsize += LISTINCREMENT;
}
L->elem[L->length] = e;
L->length++;
return OK;
}
4.插入元素
// 在第i個元素之前插入一個元素elem
int ListInsert(List *L, int i, int e) {
// 保證i合法, i可以插到最后一個元素后面, 假定插入最后一個元素后面一個元素前面
if (i<1 || i>L->length + 1) {
printf("位置不合法");
return ERROR;
}
if (L->length >= L->listsize) // 存儲空間滿, 增加分配
{
/* code */
int *newbase = (int *)realloc(L->elem, (L->listsize + LISTINCREMENT) * sizeof(int));
if (!newbase) exit(OVERFLOW); // OVERFLOW 內(nèi)存溢出
L->elem = newbase; // 新基址
L->listsize += LISTINCREMENT;
}
// 插入的位置, 這里的位置不是數(shù)組元素下標, 而是實際物理地址,也可以用下標實現(xiàn)
int *q = &(L->elem[i - 1]);
for (int *p = &(L->elem[L->length - 1]); p >= q; --p) {
*(p + 1) = *p;
}
*q = e;
L->length++;
return OK;
}
5.遍歷
void ListTraversal(List L) {
for (int i = 0; i<L.length; i++) {
printf("長度為:%d, 下標為%d的元素: %d\n", L.length ,i,L.elem[i]);
}
}
6.刪除
int ListDelete(List * L, int pos) {
if (pos<0 || pos>L->length-1) {
printf("刪除位置不合法\n");
return ERROR;
}
int *q = L->elem + pos;
int *p = L->elem + L->length - 1;
for (++q; q<=p; ++q) *(q-1) = *q;
--L->length;
return OK;
}
7.取元素
// 取出第 i 個元素并賦值給*pE;
int ListGeteElem(List p, int i, int *pE) {
if (i<1 || i>p.length) {
printf("取元素位置不合法\n");
return ERROR;
}
*pE = p.elem[i-1];
printf("第%d個元素為: %d\n", i,*pE);
return OK;
}
8.判斷是否為空
int ListIsEmpty(List L) {
if (L.length==0) {
printf("表為空\n");
return C_TRUE;
} else {
printf("表不為空\n");
return C_FALSE;
}
}
9.歸并
// La Lb 都是遞增的, 歸并后 pC 也需要遞增
void ListMerge(List La, List Lb, List *pC) {
ListInit(pC);
int i, j;
i = j = 1;
int ai;
int bj;
while (i<=La.length && j<=Lb.length) { // 保證都不越界
ListGeteElem(La, i, &ai); ListGeteElem(Lb, j, &bj);
if (ai < bj) {
ListAdd(pC, ai); // 如果ai小, 把ai放進pC, j不能加, 繼續(xù)與ai+1比較
i++;
} else {
ListAdd(pC, bj); // 同理
j++;
}
}
while (i<=La.length) {
ListGeteElem(La, i++, &ai);
ListAdd(pC,ai);
}
while (i<=Lb.length) {
ListGeteElem(Lb, i++, &ai);
ListAdd(pC,ai);
}
}
總結(jié):
學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法的過程考慮到內(nèi)存溢出, 數(shù)組越界等問題訓(xùn)練程序員所寫代碼的健壯性, 對比一些其他語言, C是直接操作內(nèi)存的, 有些語言如OC在內(nèi)存分配失敗(即alloc失敗)時一般不做任何處理, 雖然失敗的概率很小, 但就代碼的嚴謹性來說C語言更勝一籌.在學(xué)習(xí)過程中, 值得注意的兩點, 一是什么時候參數(shù)傳遞指針, 什么時候直接傳值, 我的總結(jié)是: 使用時傳值, 修改時傳遞指針; 因為使用是使用值, 而改變則需要知道修改哪塊內(nèi)存對應(yīng)的值.