第2章 順序表及其順序存儲
數據結構與算法_師大完整教程目錄(更有python、go、pytorch、tensorflow、爬蟲、人工智能教學等着你):https://www.cnblogs.com/nickchen121/p/13768298.html
一、線性表
- 線性表:屬於線性結構。有且僅有一個開始結點,有且僅有一個終端結點,其他結點為內部結點。
- 線性表的存儲:順序存儲 or 鏈式存儲
二、順序表
2.1 順序表的基本概念及描述
-
順序表:線性表采用順序存儲的方式
-
注:每個結點占用\(len\)個內存單元,\(location(k_i)\)為順序表中第\(i\)個結點\(k_i\)所占內存空間的第\(1\)個單元的地址
- \(location(k_i) = location(k_i) + len\)
- \(location(k_i) = location(k_1) + (i-1)len\)
2.2 順序表的實現
-
注:數組中下標為\(i\)的元素對應的是順序表中第\(i+1\)個結點
-
順序表的常用操作(簡單且大概率不考,因此無代碼展示,后不提醒)
- 順序表的初始化
- 順序表后部進行插入操作
- 打印順序表的各結點值
- 判斷順序表是否為空
- 查找順序表中值為x的結點位置
- 取得順序表中第i個結點的值
2.2.1 順序表的存儲結構
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAZXSIZE];
int size;
} sequence_list;
2.2.2 順序表的插入操作(算法)
-
將值為\(x\)的結點插入到第\(i\)個位置的步驟:
- 判斷順序表是否是滿的
- 判斷指定的位置是否不存在
- 將\(k_i,\cdots,k_{n-1}\)這些結點對應的數組元素依次后移一個位置,空出\(k_i\)原先的位置存放新插入的結點。(從后往前移)
- 將值為\(x\)的結點插入到第\(i\)個位置
- 順序表的長度加\(1\)
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAZXSIZE];
int size;
} sequence_list;
void insert(sequence_list *slt, datatype x, int position) {
int i;
if (slt->size == MAXSIZE) {
printf("\n順序表是滿的!沒法插入!");
exit(1);
}
if (position < 0 || position > slt->size) // 如size為10,slt->a[i-1=9]存在,既可以不用>=
{
printf("\n指定的插入位置不存在!");
exit(1);
}
for (i = slt->size; i > position; i--) {
slt->a[i] = slt->a[i - 1];
}
slt->a[position] = x;
slt->size++;
}
時間復雜度:\(O(n)\)
2.2.3 順序表的刪除操作(算法)
-
刪除順序表中第\(i\)個結點的步驟:
- 判斷順序表是否為空
- 判斷刪除位置是否存在
- 將順序表中的\(k_{i+1},\cdots,k_{n-1}\)元素依次前移一個位置。(從前往后移)
- 順序表的長度減\(1\)
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAZXSIZE];
int size;
} sequence_list;
void dele(sequence_list *slt, int position) {
int i;
if (slt->size == 0) {
pringf("\n順序表是空的!");
exit(1);
}
if (position < 0 || position >= slt->size) // 如size為10,postion為10,slt->a[i+1=11]不存在,即不可以>
{
printf("\n指定的刪除位置不存在!");
exit(1);
}
for (i = position; i < slt->size - 1; i++) // 循環到倒數第二個元素即可,因為后面是a[i+1]
{
slt->a[i] = slt->a[i + 1];
}
slt->size--;
}
時間復雜度:\(O(n)\)
三、棧
3.1 棧的基本概念及描述
- 棧:特殊的線性表。插入運算和刪除運算均在線性表的同一端進行
- 棧頂:進行插入(進棧)和刪除(出棧)的一端
- 棧底:不進行操作的一端
- 棧的性質:后進先出(先進后出)
- 出棧元素不同排列個數:\(\frac{1}{n+1}C_{2n}^{n}\)
3.2 順序棧及其實現
-
棧(順序存儲)的常用操作:
- 棧的存儲結構
- 棧初始化
- 判斷棧是否為空
- 取得棧頂結點值
- 棧的插入操作
- 棧的刪除操作
3.2.1 順序棧的存儲結構
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAXSIZE];
int top;
} sequence_stack;
3.3 棧的應用之一(括號匹配)
-
括號匹配解決步驟:
- 從左向右掃描表達式
- 遇到開括號則將遇到的開括號存放於棧中
- 遇到閉括號則查看是否與棧頂結點匹配。匹配刪除棧頂結點,不匹配說明表達式中的括號是不匹配的(結束)
- 掃描整個表達式后,棧是空的,說明表達式中的括號是匹配的;否則是不匹配的(結束)
3.4 棧的應用之二(算術表達式求值)
-
中綴表達式:操作符處於兩個操作數之間
-
前綴表達式:操作符處於兩個操作數之前
-
后綴表達式:操作符處於兩個操作數之后
-
后綴表達式求值:每遇到一個操作符,則將前面的兩個操作數執行相應的操作
-
后綴表達式求解步驟:
- 把遇到的操作數暫存於一個棧中
- 遇到操作符,則從棧中取出兩個操作數執行相應的操作,並將結果壓入棧中
- 直到對后綴表達式中最后一個操作符處理完
- 最后壓入棧中的樹就是后綴表達式的計算結果
3.4.1 中綴表達式轉換為后綴表達式步驟
-
從左到右掃描中綴表達式,如果是數字字符和圓點“.”,直接寫入后綴表達式
-
遇到開括號,將開括號壓入棧中,遇到匹配的閉括號,將棧中元素彈出並放入后綴表達式,直到棧頂元素為匹配的開括號時,彈出該開括號
-
遇到的是操作符:
- 遇到的操作符優先級<=棧頂元素,彈出棧頂元素放入后綴表達式(循環判斷)
- 遇到的操作符優先級>棧頂元素,將遇到的操作符壓入棧中
-
重復上述步驟,直到遇到結束標記“#”,彈出棧中的所有元素並放入后綴表達式數組中
-
操作符優先級:\(\#,(,+-,*/\)
-1 | 0 | 1 | 2 |
---|---|---|---|
# | ( | +- | */ |
四、隊列
4.1 隊列的基本概念及描述
- 隊列:一種特殊的線性表。插入(進隊)和刪除(出隊)操作分別在表的兩端進行
- 隊尾:插入的一端
- 對首:刪除的一端
- 隊列的性質:先進先出
4.2 順序隊列及其實現
- 隊列(順序存儲)的常用操作:
- 隊列初始化
- 判斷隊列是否為空
- 打印隊列的結點值
- 取得隊列的隊首結點值
- 隊列的插入操作
- 隊列的刪除操作
4.2.1 順序隊列的存儲結構
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAXSIZE];
int front;
int rear;
} sequence_queue;
4.3 順序循環隊列及其實現
-
普通隊列的列滿狀態指標:\(rear = MAXSIZE\)
-
循環隊列的作用:在普通隊列處於列滿狀態時,利用數組前部的空閑位置
-
循環隊列的列滿狀態指標:
- 設置一個標志:由於\(rear\)增\(1\)使\(rear = front\),為隊滿;由於\(front\)增\(1\)使\(rear = front\),隊空
- 犧牲一個數組元素空間:\((rear + 1) \% MAXSIZE = front\),隊滿;\(rear = front\),隊空
-
循環隊列(順序存儲)的常用操作(相比較普通隊列在指針增\(1\)中增加一個取模操作):
- 循環隊列的插入操作
- 循環隊列的刪除操作
4.3.1 順序循環隊列的存儲結構
#define MAXSIZE 100
typedef int datatype;
typedef struct {
datatype a[MAXSIZE];
int front;
int rear;
} sequence_queue;
五、算法設計題
5.1 順序表值為x的結點的個數(算法)
設計一個算法,求順序表中值為x的結點的個數
算法步驟:
- 設定初始值\(c\)計數
- 每找到一個值為\(x\)的結點,計數加\(1\)
- 返回\(c\)
// 順序表的存儲結構定義如下(文件名seqlist.h)
#include <stdio.h>
#define N 100 // 預定義最大的數據域空間
typedef int datatype; // 假設數據類型為整型
typedef struct {
datatype data[N]; // 此處假設數據元素只包含一個整型的關鍵字域
int length; // 線性表長度
} seqlist; // 預定義的順序表類型
// 算法countx(L, x)用於求順序表L中值為x的結點的個數
int countx(seqlist *L, datatype x) {
int c = 0;
int i;
for (i = 0; i < L->length; i++) {
if (L->data[i] == x) c++;
}
return c;
}
5.2 順序表倒置(算法)
設計一個算法,將一個順序表倒置。即,如果順序表各個結點值存儲在一維數組\(a\)中,倒置的結果是使得數組\(a\)中的\(a[o]\)等於原來的最后一個元素,\(a[1]\)等於原來的倒數第\(2\)個元素,\(\cdots\),\(a\)的最后一個元素等於原來的第一個元素
算法步驟:
- 定位最后一個元素
- 用\(t\)保留第個一元素的值,並且第一個元素和最后一個元素的值交換
- 循環到最后一個元素
// 順序表的存儲結構定義如下(文件名seqlist.h)
#include <stdio.h>
#define N 100 // 預定義最大的數據域空間
typedef int datatype; // 假設數據類型為整型
typedef struct {
datatype data[N]; // 此處假設數據元素只包含一個整型的關鍵字域
int length; // 線性表長度
} seqlist; // 預定義的順序表類型
// 算法verge(L)用於順序表倒置
void verge(seqlist *L) {
int t, i, j;
i = 0;
j = L->length - 1;
while (i < j) {
t = L->data[i];
L->data[i++] = L->data[j];
L->data[j--] = t;
}
}
5.3 有序順序表插入結點並仍然有序(真題)(算法)
已知一個順序表中的各結點值是從小到大有序的,設計一個算法,插入一個值為\(x\)的結點,使順序表中的結點仍然是從小到大有序
算法步驟:
- 從最后一個元素開始與\(x\)比較大小,元素值大於該\(x\)值,則往后挪一個位置
- 找到比\(x\)小的元素停止
- 在該元素的位置上插入\(x\)值
// 順序表的存儲結構定義如下(文件名seqlist.h)
#include <stdio.h>
#define N 100 // 預定義最大的數據域空間
typedef int datatype; // 假設數據類型為整型
typedef struct {
datatype data[N]; // 此處假設數據元素只包含一個整型的關鍵字域
int length; // 線性表長度
} seqlist; // 預定義的順序表類型
// 算法insertx(L, x)用於有序順序表插入結點並仍然有序
void insertx(seqlist *L, datatype x) {
int j;
if (L->length < N) {
j = L->length - 1;
while (j >= 0 && L->data[j] > x) {
L->data[j + 1] = L->data[j]; // 元素統一往后挪
j--;
}
L->data[j + 1] = x;
L->length++;
}
}
六、錯題集
-
長為 \(n\) 的順序表中,任意位置插入的可能次數為n+1次(包括頭前和尾后)
-
設棧 \(S\) 和隊列 \(Q\) 的初始狀態為空,元素 e1、$e\(2、\)e\(3、\)e\(4、\)e$5 和 $e$6 依次通過棧 \(S\),
一個元素出棧后即進入隊列 \(Q\),若 6 個元素出隊的序列為 $e\(2、\)e\(4、\)e\(3、\)e\(6、\)e$5 和 $e$1,則棧 \(S\)
的容量至少應該為3 -
編號為 \(1,2,3,4\) 的四列火車通過一個棧式的列車調度站,可能得到的調度結果總共有14種
- 使用公式:出棧元素不同排列個數為 \(\frac{1}{n+1}C_{2n}^{n}\)