前言
最近剛剛開始複習408資料結構,就想著把每個章節的程式碼部分彙總記錄一下,寫成一組文章,便於日後的複習和參考。
本系列整體的框架和課後習題參考了王道的複習指導。在本系列的每篇文章中,我會先把該章節所對應資料結構的基礎程式碼放上來,然後附上參考資料習題對應的題目程式碼。
線性表介紹
第一章的緒論沒有涉及到具體資料結構的程式編寫,我們在這裡就暫時跳過。從第二章中的線性表開始介紹。首先貼一下本章的整體框架:
線性表
|
|--順序儲存
| |--順序表
|
|--鏈式儲存
| |--單連結串列(指標實現)
| |--雙連結串列(指標實現)
| |--迴圈連結串列(指標實現)
| |--靜態連結串列(陣列實現)
線性表(Linear List)是資料結構的一種,一個線性表是n個具有相同特性的資料元素的有限序列。線性表按照儲存結構可分為順序表和各種連結串列,本篇文章介紹其中順序表的實現與其操作演算法的編寫。
順序表程式碼實現
#include <stdio.h>
#include <iostream>
#include <limits.h>
using namespace std;
#define INIT_SIZE 10
#define DEBUG 1
typedef int ElemType;
typedef struct {
ElemType* data;
int length;
int capacity;
} SeqList;
void init(SeqList &l) {
l.data = (ElemType*) malloc(sizeof (ElemType) * INIT_SIZE);
l.length = 0;
l.capacity = INIT_SIZE;
}
void expand(SeqList &l, int target_size) {
// Re-allocate a larger area for the list
auto new_cap = l.capacity << 1;
while (target_size > new_cap) {
new_cap = new_cap << 1;
}
l.capacity = new_cap;
if (DEBUG) {
cout << "Expanding list to " << new_cap << " bytes" << endl;
}
auto new_data = (ElemType*) malloc(sizeof (ElemType) * new_cap);
// Copy the array contents
memcpy(new_data, l.data, sizeof (ElemType) * l.length);
// Free previously used memory
free(l.data);
l.data = new_data;
}
void insert(SeqList &l, ElemType value, int pos) {
if (pos > l.length || pos < 0) { // It is ok to insert at [l.length]
cout << "Trying to insert at " << pos << " (Len: " << l.length << ")" << endl;
return;
}
int new_len = l.length + 1;
if (new_len > l.capacity) {
expand(l, new_len);
}
for (int i = l.length - 1; i >= pos; i--) {
l.data[i + 1] = l.data[i];
}
l.data[pos] = value;
l.length = new_len;
}
void remove(SeqList &l, int pos) {
if (pos >= l.length || pos < 0) { // It is NOT ok to remove at [l.length]
cout << "Trying to remove at " << pos << " (Len: " << l.length << ")" << endl;
return;
}
int new_len = l.length - 1;
for (int i = pos; i < new_len; i++) {
l.data[i] = l.data[i + 1];
}
l.length = new_len;
}
void print(SeqList &l) {
for (int i = 0; i < l.length; i++) {
cout << l.data[i] << ' ';
}
cout << endl;
}
void print_size(SeqList &l) {
cout << "Len: " << l.length << ", Cap: "<< l.capacity << endl;
}
順序表綜合應用
[01] 從順序表中刪除具有最小值的元素(假設唯一)並由函式返回被刪元素的值。空出的位置由最後一個元素填補,若順序表為空,則顯示出錯資訊並退出。
// Chap 2. 01
ElemType remove_min(SeqList &l) {
if (l.length == 0) {
cout << "List is empty" << endl;
}
ElemType cur_min = INT_MAX;
auto min_pos = -1;
for (int i = 0; i < l.length; i++) {
if (l.data[i] < cur_min) {
min_pos = i;
cur_min = l.data[i];
}
}
l.data[min_pos] = l.data[l.length - 1];
l.length -= 1;
return cur_min;
}
[02] 設計一個高效演算法,將順序表L的所有元素逆置,要求演算法的空間複雜度為O(1)。
// Chap 2. 02
void rev(SeqList &l) {
int i = 0;
// 右移一個bit就是除以2
// 奇數的話直接把0.5抹掉,正中間的元素不需要動
while (i < (l.length >> 1)) {
auto t = l.data[i];
l.data[i] = l.data[l.length - 1 - i];
l.data[l.length - 1 - i] = t;
i++;
}
}
[03] 對長度為n的順序表L,編寫一個時間複雜度為O(n)、空間複雜度為O(1)的演算法,該演算法刪除所有值為x的資料元素。
// Chap 2. 03
void remove_all(SeqList &l, ElemType value) {
int rm_cnt = 0;
for (int i = 0; i < l.length; i++) {
if (l.data[i] == value) {
rm_cnt++;
} else {
l.data[i - rm_cnt] = l.data[i];
}
}
l.length -= rm_cnt;
}