順序表思想
定義了一個順序表的結構體,存放了它的長度,容量信息,還有申請的存結點的內存的首地址。
結構體中定義的是一個二級指針,申請的內存是存放傳入結點的指針。
內存管理方面:順序表管理指表內存和指針數組內存。
結點的內存由上層自己負責。
seqlist.h
#ifndef SEQLIST_H
#define SEQLIST_H
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 // SEQLIST_H
typedef void SeqList;
typedef void SeqListNode;
傳給上層的是一個void類型,對於他們來說只是一個句柄,留給上層管理表的首地址以及結點地址。
上層測試中
Teacher* tmp = (Teacher *)SeqList_Get(list, i);
根據自己的業務來解析表傳來的結點地址
上層將順序表地址和結點地址傳給底層函數,底層函數知道具體類型是什麼,類型轉換後進行操作與處理。
所以在seqlist.c中,每個函數中都有兩句代碼。
TSeqList *tlist = NULL;
tlist = (TSeqList*)list;
seqlist.c
定義表的結構體
typedef struct _tag_SeqList
{
int length;
int capacity;
unsigned int **node;
}TSeqList;
創建表時
1.要申請表的內存,內存清零
2.根據容量申請相應的內存來存放結點指針,node 是二級指針,node[i]是指針。
3.參數置初始狀態
SeqList* SeqList_Create(int capacity)
{
int ret =0;
TSeqList *tmp = NULL;
tmp=(TSeqList*)malloc(sizeof(TSeqList));
if(tmp == NULL)
{
ret = -1;
printf("func SeqList_Create() err:create %d\n",ret);
return NULL;
}
memset(tmp,0,sizeof(TSeqList)); //需要包含string.h頭文件
tmp->node=(unsigned int **)malloc(sizeof(unsigned int *)*capacity);
//這裏順序表只是存了結點的地址以保證能訪問,但是不把節點數據及內存併入順序表中
//這樣順序表只管理自己申請的這倆內存,而節點的內存還是由上層管理
//這也是destroy中只用釋放申請的tlist->node及tlist本身的內存
if(tmp->node == NULL)
{
ret = -2;
printf("func SeqList_Create() err:malloc %d\n",ret);
return NULL;
}
tmp->capacity = capacity;
tmp->length = 0;
return tmp;
}
順序表銷燬時要釋放內存
順序表清空時主要是長度要置零以重新存數據。
這裏可加memset? 我覺得是要加,主動清零內存
void SeqList_Destroy(SeqList* list)
{
TSeqList *tlist = NULL;
if(list == NULL)
{
return ;
}
tlist = (TSeqList*)list;
if(tlist->node!=NULL)
{
free(tlist->node);
}
free(tlist);
return;
}
void SeqList_Clear(SeqList* list)
{
TSeqList *tlist = NULL;
if(list == NULL)
{
return ;
}
tlist= (TSeqList*)list;
//memset(tlist->node,0,sizeof(unsigned int *)*tlist->capacity);
tlist->length = 0;
return;
}
獲取長度和容量沒什麼,返回數據就可以
int SeqList_Length(SeqList* list)
{
TSeqList *tlist = NULL;
if(list == NULL)
{
return -1;
}
tlist = (TSeqList*)list;
return tlist->length;
}
int SeqList_Capacity(SeqList* list)
{
TSeqList *tlist = NULL;
if(list == NULL)
{
return -1;
}
tlist = (TSeqList*)list;
return tlist->capacity;
}
獲取結點也不涉及移動,直接返回相應位置的節點即可
SeqListNode* SeqList_Get(SeqList *list,int pos)
{
int i =0,ret =0;
TSeqList *tlist = NULL;
SeqListNode *s_ret = NULL;
if(list == NULL || pos <0)
{
ret = -1;
printf("funt SeqList_Get() err %d\n",ret);
return NULL;
}
tlist = (TSeqList*)list;
s_ret =(SeqListNode*)tlist->node[pos];
return s_ret;
}
插入和刪除一個是要移動, 二是要注意長度++或 - -,同時要判斷位置是否合法,判滿等。
int SeqList_Insert(SeqList* list, SeqListNode *node,int pos)
{
int i =0,ret =0;
TSeqList *tlist = NULL;
if(list == NULL || node == NULL || pos <0) //爲什麼不判pos>length呢? 因爲只要表沒滿,可以處理讓他插在最尾
{
ret = -1;
printf("func SeqList_Insert() err %d\n",ret);
return ret;
}
tlist = (TSeqList*)list;
//先判滿
if(tlist->length == tlist->capacity)
{
ret = -2;
printf("func SeqList_Insert() (tlist->length == tlist->capacity) err: %d\n",ret);
return ret;
}
//容錯修正
if(pos>tlist->length)
{
pos = tlist->length;
}
//先後移元素
for(i=tlist->length;i>pos;i--)
{
tlist->node[i]=tlist->node[i-1];
}
tlist->node[i] = node;
tlist->length++;
return 0;
}
SeqListNode* SeqList_Delete(SeqList* list ,int pos)
{
int i=0;
TSeqList *tlist = NULL;
SeqListNode *ret = NULL;
if(list == NULL || pos < 0) //刪除位置大於表長度的情況如何處理,返回的是NULL
//但存在clear後未覆蓋刪除原有數據的問題吧?所以應優化clear
{
printf("func SeqList_Delete() err");
return NULL;
}
tlist = (SeqList*)list;
ret = (SeqListNode*)tlist->node[pos];
for(i=pos+1;i<tlist->length;i++)
{
tlist->node[i-1]=tlist->node[i];
}
tlist->length--;
return ret;
}
插入操作時要先判滿和容錯修正,然後移動,再插入
刪除操作時先緩存要彈出的結點,再移動。返回結點
測試用例
ret = SeqList_Insert(list, (SeqListNode*)&t5, 0); //頭插法
for (int i = 0; i < SeqList_Length(list); i++)
{
Teacher* tmp = (Teacher *)SeqList_Get(list, i);
if (tmp == NULL)
{
return;
}
printf("tmp->age:%d", tmp->age);
}
一個是頭插法。 數據可以傳入結構體等類型
再一個是解析彈出的結點,根據上層的數據類型進行類型轉換後解讀。