? ? ? ? 在C語言學習到一定階段之后,接下來我們就進入到了數據結構的部分內容。
目錄
數據結構與線性表
順序表
? ? ? ? 順序表分類:
? ? ? ? 接下來我們要寫一段代碼實現動態順序表。
首先我們需要準備三個文件:
1.接下來我們要定義一個數據表
2.當創建號我們的順序表之后,我們要對他進行初始化
3.而動態內存創建后就必須有銷毀
4.接下來我們要對順序表進行各種操作:增刪插改
尾部插入數據
?頭部插入數據
尾部刪除數據
頭部刪除數據:
指定位置之前插入數據
指定位置查找數據
完整的代碼:
數據結構與線性表
? ? ? ? 數據就像草原的一群羊一樣,如果你要在草原上找到一個叫做“咩咩”的羊很難,但是如果你要找一頭“3號”羊是比較簡單的。數據結構就是像羊圈管理羊群一樣管理數據。
? ? ? ? 因此可以說,數據結構就是計算機存儲和組織數據的方式。
? ? ? ? 數組就是一種很基礎的數據結構,用來存儲一群同類型的數據。但是隨著我們要對數據進行的操作愈加復雜(訪問、修改、插入數據等等等等),頻繁的訪問數組已經嚴重影響了計算機運行的效率,因此簡單的數組已經無法滿足我們的需要了,因此我們需要進入第一個數據結構類型——線性表。
? ? ? ? 線性表是n個具有相同特性的數據元素的有限序列,是一種廣泛應用的數據結構,常見的線性表有:順序表、鏈表、棧、隊列、字符串等等。
? ? ? ? 但是注意線性表在邏輯上是連續的線性結構,但是物理上不一定是連續的。線性表在物理上儲存的時候通常以數組和鏈式結構的形式儲存。
順序表
? ? ? ? 順序表在底層上是數組。
? ? ? ? 那么既然已經有數組了,我們為什么需要順序表呢?舉例說明:
int a[100]={1,2,3,4,...X...};
????????對于如上所示的100個元素的數組a。如果我們要修改其中一個數字就找先遍歷數組找到對應元素修改,如果要插入和刪除數組的話,都需要遍歷這個100個元素的數組。這個效率顯然是很低的。因此我們需要一個更好用、效率更高的工具:順序表。
? ? ? ? 順序表是在數組的基礎上加上了增刪查改等方法的一種儲存形式。順序表的特性是在物理結構上連續,在邏輯結構上也連續。
? ? ? ? 這是一個固定長度的數組
int a[10]={0};
? ? ? ? 這是動態內存開辟出來的數組,確定大小后再去申請。
int *arr
? ? ? ? 順序表分類:
????????靜態順序表:
?
?
Stack SeqList
{int arr[100];//定長數組int size;//順序表當前有效的數據個數}??
? ? ? ? 動態順序表:
?
Stack Seqlist
{int *arr;//int size//有效數字個數int capacity;//空間大小
};?
相較于靜態順序表,動態順序表可以動態增容。
? ? ? ? 接下來我們要寫一段代碼實現動態順序表。
首先我們需要準備三個文件:
Seqlist.c——實現順序表的方法
Seqlist.h——順序表結構,順序表聲明,方法
test.c——測試代碼
1.接下來我們要定義一個數據表
2.當創建號我們的順序表之后,我們要對他進行初始化
這里我們用傳值返回就會報錯,原因:傳值返回是值拷貝,但是這里s1沒有值。
所以這里要用傳址返回。
3.而動態內存創建后就必須有銷毀
4.接下來我們要對順序表進行各種操作:增刪插改
聲明:
尾部插入數據
原理展示:
要申請多大的空間?
?增容一般是2到3倍(2倍更常見)。因為一次性給太多空間就會像靜態順序表那樣浪費大量空間而得不償失,而過少就會導致需要頻繁地擴容而導致程序運行效率過低
?尾部插入數據函數一:
尾部插入數據函數二:
因此這部分的代碼為:
但是這個結構是比較脆弱的,如果用戶輸入了這么一個東西,它就會報錯:讀取訪問權限沖突。
SLPushback(NULL, 5);
?所以我們要對這個函數進行改造。
溫柔的方式
//溫柔的解決方式
if (ps->arr == NULL)
{return;
}
if (ps->size == ps->capacity)//空間不夠了
{
//code
}
暴力的方式
//暴力判斷
assert(ps->arr!= NULL);
//等價于assert(ps)
尾部插入的代碼:
//尾部插入數據
void SLPushback(SL* ps, STDatatype x)
{//暴力判斷assert(ps->arr!= NULL);//等價于assert(ps)if (ps->size == ps->capacity)//空間不夠了{//申請空間//要增容只能用realloc函數,不能用malloc和callocint newcapacity = ps->capacity == 0 ? 4:2*ps->capacity;STDatatype*tmp = realloc(ps->arr, newcapacity * 2 * sizeof(STDatatype));//tmp指的是結構體當中的數組arrif (tmp==NULL )//realloc可能申請失敗所以要判斷{perror("realloc error");return 1;//程序推出}//申請成功ps->arr = tmp;ps->capacity = newcapacity;}ps->arr[ps->size++] = x;
}
?頭部插入數據
?原理圖:
如圖所示,如果要申請空間挪動數據的話從最后一位開始
?代碼為:
尾部刪除數據
原理圖:
????????
代碼:?
??
頭部刪除數據:
原理圖:
代碼:??
指定位置之前插入數據
原理:
代碼;?指定位置刪除數據
原理圖:
? ? ? ? 代碼:
指定位置查找數據
查找數據相對來說比較簡單,這里受限于篇幅,只講解暴力查找的算法,即循環遍歷數組
完整的代碼:
Seqlist.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include<assert.h>
//定義順序表的結構//#define N 100
//靜態順序表
//struct SeqList
//{
// int arr[N];
// int size;//有效數據個數
//};
typedef int STDatatype;
//這里是為了將來方便替換數組存儲數據的類型
// 如果將來需要替換數據類型為char,只需要將int改為char即可//動態順序表
typedef struct SeqList
{STDatatype* arr; int size;//有效數據個數int capacity;//順序表空間大小
}SL;//這是為了將來為了方便調用結構體//順序表初始化
void SLInit(SL* ps);//順序表的銷毀
void SLDestory(SL* ps);
//校驗順序表空間夠不夠
void SLCheckCapacity(SL* ps);
//順序表的打印
void SLPrint(SL s);
//順序表頭部/尾部插入數據
void SLPushback(SL*ps, STDatatype x);//尾部插入數據
void SLPushfront(SL*ps, STDatatype x);//頭部插入數據void SLPopback(SL*ps); //尾部刪除數據
void SLPopfront(SL*ps);//頭部刪除數據
//指定位置之前插入數據/刪除數據/查找數據
void SLInsert(SL* ps, int pos,STDatatype x);//插入數據
//ps表示在ps所在的數組里插入數據
//pos在指定順序表里下標的位置
//x為插入的數據
void SLDelete(SL* ps,int pos);//刪除數據
int SLFind(SL* ps, STDatatype x);//查找數據
Seqlist.c
#define _CRT_SECURE_NO_WARNINGS
#include"Seqlist.h"
//Seqlist.c——實現順序表的方法
//Seqlist.h——順序表結構,順序表聲明,方法
//順序表的初始化
void SLInit(SL *ps)
{ps->arr = NULL;//需要包含頭文件<stdlib.h>ps->size=ps->capacity=0;}
//順序表的銷毀
void SLDestory(SL*ps)
{if (ps->arr)//判斷數據表的數組不為空{free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}
//校驗順序表空間夠不夠
void SLCheckCapacity(SL* ps)
{if (ps->size == ps->capacity)//空間不夠了{//申請空間//要增容只能用realloc函數,不能用malloc和callocint newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDatatype* tmp = realloc(ps->arr, newcapacity * 2 * sizeof(STDatatype));//tmp指的是結構體當中的數組arrif (tmp == NULL)//realloc可能申請失敗所以要判斷{perror("realloc error");return 1;//程序推出}ps->arr = tmp;ps->capacity = newcapacity;}
}
//順序表的打印
void SLPrint(SL s)
{for (int i = 0;i < s.size;i++){printf("%d ", s.arr[i]);}printf("\n");
}
//尾部插入數據
void SLPushback(SL* ps, STDatatype x)
{//溫柔的方式// if(ps == NULL)// {// return;// }//暴力判斷assert(ps);//等價于assert(ps->arr!= NULL);//校驗空間夠不夠SLCheckCapacity(ps);ps->arr[ps->size++] = x;
}
//頭部插入數據
void SLPushfront(SL* ps, STDatatype x)
{assert(ps);SLCheckCapacity(ps);//先讓順序表中整體的數據向后移動一位。for (int i = ps->size - 1;i >=0;i--){ps->arr[i+1]=ps->arr[i];//最后為arr[1]=arr[0]}ps->arr[0] = x;ps->size++;
}//尾部刪除數據
void SLPopback(SL* ps, STDatatype x)
{assert(ps);assert(ps->size != 0);//順序表不為空//這段代碼是否存在無所謂:ps->arr[ps->size - 1] = -1;--ps->size;
}
//頭部刪除數據
void SLPopfront(SL* ps)
{assert(ps);assert(ps->size != 0);//數據表整體向前移動一位for (int i = 0;i <= ps->size - 1;i++){ps->arr[i] = ps->arr[i+1];}ps->size--;
}
//指定位置之前插入數據
void SLInsert(SL* ps, int pos, STDatatype x)
{assert(ps);//pos必須>=0且<=sizeassert(pos>=0&&pos<=ps->size);SLCheckCapacity(ps);//校驗空間夠不夠//讓pos位置及之后的數據集體向后一位for (int i = ps->size-1;i >= pos;i--){ps->arr[i+1] = ps->arr[i];//最后為arr[pos+1]=arr[pos]}//pos位置空出來了ps->arr[pos] = x;ps->size++;
}
//指定位置刪除數據
void SLDelete(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos <ps->size);for (int i=pos;i<ps->size-1;i++){ps->arr[i] = ps->arr[i+1];//最后為arr[size-2]=arr[size-1]}ps->size--;}
//指定位置查找數據
int SLFind(SL* ps, STDatatype x)
{assert(ps);//遍歷數組查找for (int i = 0;i <= ps->size - 1;i++){if (x == ps->arr[i]){//找到了return i;}else {continue;}}return -1;//無效的下標表示沒找到
}
test.c
?
#define _CRT_SECURE_NO_WARNINGS
#include"Seqlist.h"
void SLtest1()
{SL s1;SLInit(&s1);//這里我們不能用傳值返回//傳值返回是值拷貝,但是這里s1沒有值//所以這里要用傳址返回//增刪查改操作。//順序表的插入//測試尾插SLPushback(&s1,1);SLPushback(&s1,2);SLPushback(&s1,3);SLPushback(&s1,4);//測試頭插SLPushfront(&s1, 5);SLPushfront(&s1, 6);SLPrint(s1);// 測試頭刪SLPopfront(&s1);SLPopfront(&s1);SLPrint(s1);// 測試尾刪SLPopback(&s1);SLPopback(&s1);SLPrint(s1);//順序表的銷毀SLDestory(&s1);
}
void SLtest2()
{SL s1;//初始化SLInit(&s1);//尾部插入數據SLPushback(&s1, 1);SLPushback(&s1, 2);SLPushback(&s1, 3);SLPushback(&s1, 4);//在指定位置之前插入數據SLInsert(&s1, 0, 99);SLInsert(&s1, s1.size, 88);SLPrint(s1);//99 1 2 3 4 88//刪除指定位置的數據SLDelete(&s1, 2);SLPrint(s1);//查找指定數據int F=SLFind(&s1, 3);if (F >= 0){printf("找到了,位置是%d\n",F);}else{printf("沒找到\n");}銷毀SLDestory(&s1);
}
int main()
{SLtest1();SLtest2();return 0;
}
????????順序表的內容并沒有結束,但是受限于篇幅原因我只能先到這里了,感謝各位讀者朋友的閱讀,求一個贊,謝謝。