C語言數據結構----算法基本知識和靜態表

一、算法的基本知識

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;	
}

以下都是我同學的經典總結:大家可以借鑑。

本節的代碼是一個可以適合各種類型的順序表,之所以能夠適合各種類型,是因爲它在順序表中保存的是元素的地址(其實就是一個指針數組)。

2.代碼中的描述順序表的結構體中的元素介紹:length是順序表中有元素的個數、capacity是順序表的容量、node是順序表的頭地址(也是這個指針數組的頭地址)、還有一個就是pos,pos是在刪除和插入的時候使用的一個參數,它代表的是插入到順序表位置的下標(數組的下標 是從0開始的 這個很要注意)。順序表中有length個元素 下標是從0到length-1的。要注意的是 操作順序表不同功能函數的pos的允許範圍是不一樣的。
3.本節代碼對於函數參數的合法性判斷是極其重視的,這個規範是值得學習的。
4.本節代碼中對於順序表的操作函數,凡是外界輸入的,和輸出到外界的,都是void *類型的,這樣就保證了只有在這些操作函數中才能去改變   描述順序表的結構體裏面的值,在其他文件的函數中接受到的都是void *類型,無法直接給這個結構體中的值進行改變,這樣的封裝,保證了代碼的安全性。
5.對於本節代碼最值得思考的地方,常見的順序表是typedef一個A類型,然後在順序表中定義一個這個A類型的數組和length順序表元素個數,這個順序表中是好多個A類型的順序集合,佔用空間的大小是sizeof(A)*capacity。而本節的順序表中是好多個unsigned int *地址類型的順序集合,表中只有地址,第一節省了順序表的空間,第二這樣可以變相的保存不同類型的數據,第三它實現了 順序表(即數據結構) 和 我們打算利用的數據(即元素)的分離。例如:linux內核鏈表(一個雙向循環鏈表)就是一套單獨的鏈表體制,這個鏈表用在很多機制上面,它就是變相的存儲了好多類型的數據,並且實現了鏈表和數據的分離。

 關於老唐的後期補充:

我們先來更改一下測試代碼:

#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開始算的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章