一、算法的基本知識
1.編寫程序的基本規則
①儘量少使用內存空間。②儘量少的代碼量解決問題。
2.數據的特點
①必須能夠輸入到計算機。②必須能夠被程序處理。
3.數據的幾個概念
①數據元素:組成數據的基本單位。②數據項:一個元素由若干個數據組成。③數據對象:性質相同的數據元素的集合。
如下圖:
4.數據的結構的組成
(1)邏輯結構:集合結構、線性結構、樹形結構、圖形結構
(2)物理結構:
①順序存儲結構:將數據存儲在連續的存儲單元裏。(例如數組)
②鏈式存儲結構:將數據存儲在任意的存儲單元裏,通過保存地址的方式找到相關聯的數據。
5.數據結構靜態描述了數據元素之間的關係。高效的程序需要在數據結構的基礎上設計合理的算法。
算法的概念:是特定的問題的求解步驟的描述。在計算機中表現爲指令的有序序列。
6.算法的特性:(1)輸入(2)輸出(3)有窮性(4)確定性(5)可行性
7.程序 = 數據結構 + 算法。
8.算法的效率表示方法採用大O表示法。
概念:大O的表示法(O()),也就是查看最高次項的表示法。分析的算法的時間複雜度,指的都是算法的最壞時間複雜度。
算法的空間複雜度:
S(n) = O(f(n))
大O表示方法如下:
二、線性表的基本概念
1.定義:
(1)線性表是由0個或者多個數據元素組成的集合。
(2)線性表中數據元素之間是有順序的。
(3)線性表中的數據元素的個數必須有限。
(4)線性表中的數據元素類型必須相同。
線性表是具有相同類型的n個數據元素的有限序列。
2.特性:
(1)第一個元素只有一個後繼
(2)線性表的最後一個元素只有一個前驅
(3)除了a0和an以外其他的元素都應該既有前驅又有後繼
(4)線性表能夠逐項訪問和順序存取
3.線性表的相關操作:
(1)創建
(2)銷燬
(3)得到長度
(4)從線性表刪除一個元素
(5)從線性表添加一個元素
(6)在線性表的特定位置添加元素
三、線性表的操作和具體實現
1.線性表的順序存儲定義:用一段地址連續的內存單元依次存儲線性表的數據元素。
2.線性表的具體操作:
(1).h文件
#ifndef _1_H_
#define _1_H_
//這裏是一個類型封裝,對外接口都是void*。
typedef void SeqList;
typedef void SeqListNode;
//定義一個線性表的最大容量
SeqList* SeqList_Create(int capacity);
void SeqList_Destroy(SeqList* list);
void SeqList_Clear(SeqList* list);
int SeqList_Length(SeqList* list);
//返回順序表的最大容量
int SeqList_Capacity(SeqList* list);
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
SeqListNode* SeqList_Get(SeqList* list, int pos);
SeqListNode* SeqList_Delete(SeqList* list, int pos);
#endif
(2)數據結構的.c文件
#include <stdio.h>
#include <malloc.h>
#include "1.h"
typedef unsigned int TSeqListNode; //存放的是數據地址,所以使用unsigned int
typedef struct student
{
int length; //順序表的長度
int capacity;//順序表的容量(自己定義,也是一個缺陷)
TSeqListNode *node; //用於順序表中的數據進出
}TSeqList;
SeqList* SeqList_Create(int capacity)
{
TSeqList * ret = NULL;
if (capacity >= 0)
{
ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
}
if (ret != NULL)
{
ret -> length = 0;
ret -> capacity = capacity;
ret -> node = (TSeqListNode*)(ret + 1);
}
return ret;
}
void SeqList_Destroy(SeqList* list)
{
free (list);
}
void SeqList_Clear(SeqList* list)
{
TSeqList* slist = (TSeqList*)list;
if (slist != NULL)
{
slist -> length = 0;
}
}
int SeqList_Length(SeqList* list)
{
TSeqList * slist = (TSeqList *)(list);
int ret = -1;
if (slist != 0)
{
ret = slist -> length;
}
return ret;
}
int SeqList_Capacity(SeqList* list)
{
TSeqList* slist = (TSeqList*)list;
int ret = -1;
if (slist != NULL)
{
ret = slist -> capacity;
}
return ret;
}
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
{
TSeqList *slist = (TSeqList*)list;
int ret = (slist != NULL);
int i = 0;
ret = ret && (slist->length + 1 <= slist -> capacity);
ret = ret && (pos >= 0);
if (ret)
{
if (pos >= slist -> length) //如果插入數據的位置比表的長度還長,那麼就默認到表的結尾。
{
pos = slist -> length;
}
for (i = slist -> length; i > pos; i--)
{
slist->node[i] = slist->node[i - 1];
}
slist -> node[i] = (TSeqListNode)node; //對要插入的地址進行強制類型轉換
slist -> length++;
}
return ret; //1成功,0失敗。
}
SeqListNode* SeqList_Get(SeqList* list, int pos)
{
TSeqList * slist = (TSeqList*)list;
SeqListNode * ret = NULL;
if (slist != NULL && pos >= 0 && pos < slist -> length)
{
ret = (SeqListNode*)(slist->node[pos]);
}
return ret;
}
/*不做過多的註釋了,個人覺得刪除函數需要注意的地方更多。*/
SeqListNode* SeqList_Delete(SeqList* list, int pos)
{
TSeqList *slist = (TSeqList*)list;
SeqListNode * ret = SeqList_Get (list, pos);
int i = 0;
if (ret != NULL)
{
//ret = slist -> node[pos];
for (i=pos+1; i<slist->length; i++)
{
slist -> node[i - 1] = slist-> node[i];
}
slist -> length--;
}
return ret;
}
(3)測試部分代碼.c文件
#include <stdio.h>
#include "1.h"
#include <stdlib.h>
int main()
{
SeqList *list = SeqList_Create(9);
int i = 0;
int j = 1;
int k = 2;
int x = 3;
int y = 4;
int z = 5;
int index = 0;
SeqList_Insert(list, &i,0);
SeqList_Insert(list, &j,0);
SeqList_Insert(list, &k,0);
SeqList_Insert(list, &x,0);
SeqList_Insert(list, &y,0);
SeqList_Insert(list, &z,0);
for (index = 0; index < SeqList_Length(list); index++)
{
int *p = (int*)SeqList_Get(list, index);
printf ("%d\n",*p);
}
printf("\n");
while( SeqList_Length(list) > 0 )
{
int* p = (int*)SeqList_Delete(list, 0);
printf("%d\n", *p);
}
SeqList_Destroy(list);
return 0;
}
以下都是我同學的經典總結:大家可以借鑑。
本節的代碼是一個可以適合各種類型的順序表,之所以能夠適合各種類型,是因爲它在順序表中保存的是元素的地址(其實就是一個指針數組)。
關於老唐的後期補充:
我們先來更改一下測試代碼:
#include <stdio.h>
#include "1.h"
#include <stdlib.h>
int main()
{
SeqList *list = SeqList_Create(9);
int i = 0;
int j = 1;
int k = 2;
int x = 3;
int y = 4;
int z = 5;
int index = 0;
SeqList_Insert(list, &i,0);
SeqList_Insert(list, &j,0);
SeqList_Insert(list, &k,0);
SeqList_Insert(list, &x,0);
SeqList_Insert(list, &y,0);
SeqList_Insert(list, &z,0);
for (index = 0; index < SeqList_Length(list); index++)
{
int *p = (int*)SeqList_Get(list, index);
printf ("%d\n",*p);
}
printf("\n");
int* p = (int*)SeqList_Delete(list, 5);
printf("%x\n", SeqList_Get(list, 5));
SeqList_Destroy(list);
return 0;
}
這段代碼實現的功能:我們首先在順序表中插入6個元素分別是0,1,2,3,4,5.然後我們執行打印操作,在對第5個位置的元素進行刪除,這樣實際上我們刪除的是數值爲5的元素,也就是刪除順序表末尾的元素,然後我們再獲取已經刪除了的元素的地址,使用SeqList_Get函數,這個時候我們打印出來的是0,而老唐原來的程序打印出來的是一個地址,但是假如打印出來的是地址,但是這個數據元素我們已經刪除了它還可以打印出來地址,這是不對的,所以我們要注意get函數中的條件檢測。get函數中的安全性檢測,如下所示:
SeqListNode* SeqList_Get(SeqList* list, int pos)
{
TSeqList * slist = (TSeqList*)list;
SeqListNode * ret = NULL;
if (slist != NULL && pos >= 0 && pos < slist -> length)
{
ret = (SeqListNode*)(slist->node[pos]);
}
return ret;
}
這裏一定要注意 pos < slist->length,而不是pos<=slist->length,因爲我們的長度是從1開始算的,而我們的位置卻是從0開始算的。