Introduction
最基本的資料結構就是由線性排列儲存的,我們可以分成利用陣列或是鏈結串列,這2者的差別在於陣列在記憶體中是連續排列的,而鏈結串列在記憶體中不是連續排列的。
鏈結串列指的是將多個不連續的資料節點,透過指標方式串連起來的資料結構,鏈結串列由多個節點組成,每個節點至少包含資料以及儲存下一個位址的指標
鏈結串列因為可以動態的增加和刪除節點,因此在頻繁需要加入和刪除的時候會很有用,因為當陣列要加入或刪除時,必須要移動節點才不會浪費空間
Dynamic Array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct array {
void* arr;
size_t data_size;
size_t length;
} array;
array* arr_init(size_t data_size) {
array* temp = NULL;
temp = malloc(sizeof *temp);
if(temp == NULL) return NULL;
temp->arr = NULL;
temp->data_size = data_size;
temp->length = 0;
return temp;
}
size_t arr_lenght(array* arr) {
if(arr == NULL) return -1;
return arr->length;
}
void* arr_front(array* arr) {
if(arr == NULL) return NULL;
return (char*)(arr->arr);
}
void* arr_end(array* arr) {
if(arr == NULL) return NULL;
return (char*)(arr->arr) + ((arr->length - 1) * arr->data_size);
}
bool arr_push_back(array* arr, void* elem) {
void* temp = NULL;
temp = realloc(arr->arr, arr->data_size * (arr->length + 1));
if(temp == NULL) return false;
arr->arr = temp;
memcpy(((char*) arr->arr) + (arr->length) * arr->data_size, elem, arr->data_size);
arr->length = arr->length + 1;
return true;
}
void* arr_pop_back(array* arr) {
void* temp = NULL;
void* data = NULL;
if(arr == NULL) return NULL;
if(arr->length == 0) return NULL;
data = arr_end(arr);
temp = realloc(arr->arr, (arr->length - 1) * arr->data_size);
if(temp == NULL) return NULL;
arr->arr = temp;
arr->length = arr->length - 1;
return data;
}
bool arr_insert(array* arr, int index, void* elem) {
if(index > arr->length) return false;
void* temp = NULL;
temp = realloc(arr->arr, (arr->length + 1) * arr->data_size);
if(temp == NULL) return false;
for(int j=index; j<arr->length; j++) {
void* after = ((char*) temp) + (j+1) * arr->data_size;
void* before = ((char*) temp) + (j) * arr->data_size;
memcpy(after, before, arr->data_size);
}
memcpy(((char*) temp) + (index) * arr->data_size, elem, arr->data_size);
arr->arr = temp;
arr->length += 1;
printf("Array growing : size = %ld\n", arr->length);
return true;
}
void* arr_erase(array* arr, int index) {
if(arr->length == 0) return NULL;
if(index >= arr->length || index < 0) return NULL;
void* temp = NULL;
void* data = NULL;
temp = realloc(arr->arr, (arr->length - 1) * arr->data_size);
if(temp == NULL) return NULL;
for(int j=index; j<arr->length-1; j++) {
void* after = ((char*) arr->arr) + (j+1) * arr->data_size;
void* before = ((char*) temp) + (j) * arr->data_size;
memcpy(before, after, arr->data_size);
}
data = ((char*) temp) + (index) * arr->data_size;
arr->arr = temp;
arr->length -= 1;
return data;
}
void arr_empty(array* arr) {
arr->length = 0;
}
bool arr_is_empty(array* arr) {
return (arr->length == 0);
}
array* linkedList_create_from_list(void* arr, int len, size_t type_size) {
array* temp = arr_init(type_size);
if(temp == NULL) return NULL;
for(int i=1; i<len; i++) {
arr_push_back(arr, (((char*)arr) + (i) * temp->data_size));
}
return temp;
}
void arr_free(array *arr) {
free(arr);
}
void* arr_get(array* arr, int index) {
if(arr_is_empty(arr)) return NULL;
if(index > arr_lenght(arr) || index < 0) return NULL;
return ((char*) arr->arr) + (index) * arr->data_size;
}
int main(int argc, char const *argv[]) {
array* arr = arr_init(sizeof(char*));
char *names[] = { "John", "Mary", "George", "Bob", "Tony" };
arr_insert(arr, 0, &names[4]);
arr_insert(arr, 1, &names[3]);
arr_insert(arr, 2, &names[2]);
arr_insert(arr, 3, &names[1]);
arr_insert(arr, 4, &names[0]);
char* name;
for (int i = 0; i < arr->length; i++) {
memcpy(&name, arr_get(arr, i), sizeof(char*));
printf("name[%d]=%s\n", i, name);
}
arr_erase(arr, 3);
printf("\n\n");
arr_push_back(arr, &names[1]);
for (int i = 0; i < arr->length; i++) {
memcpy(&name, arr_get(arr, i), sizeof(char*));
printf("name[%d]=%s\n", i, name);
}
arr_free(arr);
return 0;
}
Time Complexity
OPERATION | WORST CASE | AVERAGE CASE | BEST CASE |
Resize | O(N) | O(N) | O(N) |
Add | O(1) | O(1) | O(1) |
Add at an index | O(N) | O(N) | O(1) |
Delete | O(1) | O(1) | O(1) |
Delete at an index | O(N) | O(N) | O(1) |
單向鏈結串列
結構定義
加入動作
加入於串列的前端
加入於串列的後端
隨機加入某一節點
刪除動作
刪除串列前端的節點
刪除串列的尾端節點
隨機刪除某一節點
將兩串列相連接
將串列反轉
計算串列的長度
完整的程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void insertFunction(void);
void sortFunction(void);
void deleteFunction(void);
void displayFunction(void);
void modifyFunction(void);
void flushBuffer(void);
void insertAt(void);
void search();
void reverseList();
struct student* Concat(struct student*);
struct student* CreateList(void);
struct student* kthNodeFromEnd(struct student*, int);
struct student* middleNode(struct student*);
int IsLinkedListLengthEven(struct student*);
struct student {
char name[20];
int score;
struct student* next;
};
struct student* ptr, * head, * current, * prev;
int main()
{
head = (struct student*)malloc(sizeof(struct student));
head->next = NULL;
int option1;
do {
printf("\n");
printf("****************************************\n");
printf(" 1.insert\n");
printf(" 2.Insert an element at a particular position\n");
printf(" 3.delete\n");
printf(" 4.display\n");
printf(" 5.Search for an element in list\n");
printf(" 6.Reverse the list\n");
printf(" 7.Concate Two Linked Lists\n");
printf(" 8.modify\n");
printf(" 9.Nth node from the end of a Linked List\n");
printf(" 10.find the middle of the linked list\n");
printf(" 11.display a Linked List from the end\n");
printf(" 12.Linked List length is even or odd\n");
printf(" 13.quit\n");
printf("****************************************\n");
printf(" Please enter your choice (1-13)...");
scanf("%d", &option1);
switch (option1) {
case 1:
insertFunction();
break;
case 2:
insertAt();
break;
case 3:
deleteFunction();
break;
case 4:
displayFunction();
break;
case 5:
search();
break;
case 6:
reverseList();
printf("\nList reversed\n");
break;
case 7:
head = Concat(head);
displayFunction();
break;
case 8:
modifyFunction();
break;
case 9:
int k;
scanf("%d", &k);
struct student* kthNode;
kthNode = kthNodeFromEnd(head, k);
printf("\nkth node from end in the linked list is %d and %s", kthNode->score, kthNode->name);
break;
case 10:
struct student* middle;
middle = middleNode(head);
printf("\n Middle in the linked list is %d and %s", middle->score, middle->name);
break;
case 11:
int even_odd;
even_odd = IsLinkedListLengthEven(head);
if (even_odd)
{
printf("\nLinked list in even or odd: even\n");
}
else
{
printf("\nLinked list in even or odd: odd\n");
}
break;
case 12:
printf("\n");
return 0;
case 13:
break;
default:
break;
}
} while(option1 != 13);
}
void insertFunction(void)
{
char sTemp[4];
ptr = (struct student*)malloc(sizeof(struct student));
printf(" Student name : ");
scanf("%s", ptr->name);
printf(" Student score: ");
scanf("%d", &ptr->score);
flushBuffer();
sortFunction();
}
/* 以分數高低由大到小排列 */
void sortFunction(void)
{
//插入資料
prev = head;
current = head->next;
while ((current != NULL) && (current->score > ptr->score)) {
prev = current;
current = current->next;
}
ptr->next = current;
prev->next = ptr;
}
void deleteFunction(void)
{
char delName[20];
printf(" Delete student name: ");
scanf("%s", delName);
flushBuffer();
prev = head;
current = head->next;
while ((current != NULL) && (strcmp(current->name, delName) != 0)) {
prev = current;
current = current->next;
}
if (current != NULL) {
prev->next = current->next;
free(current);
printf(" Student %s has been deleted\n", delName);
}
else
printf(" Student %s not found\n", delName);
}
void modifyFunction(void)
{
char nTemp[20], sTemp[4];
printf(" Modify student name: ");
scanf("%s", nTemp);
flushBuffer();
prev = head;
current = head->next;
while ((current != NULL) && (strcmp(current->name, nTemp) != 0)) {
prev = current;
current = current->next;
}
if (current != NULL) {
printf(" **************************\n");
printf(" Student name : %s\n", current->name);
printf(" Student score: %d\n", current->score);
printf(" **************************\n");
printf(" Please enter new score: ");
scanf("%s", sTemp);
flushBuffer();
prev->next = current->next;
free(current);
//重新加入
ptr = (struct student*)malloc(sizeof(struct student));
strcpy(ptr->name, nTemp);
ptr->score = atoi(sTemp);
ptr->next = NULL;
prev = head;
current = head->next;
while ((current != NULL) && (current->score > ptr->score)) {
prev = current;
current = current->next;
}
ptr->next = current;
prev->next = ptr;
printf(" Student %s has been modified\n", nTemp);
}
else
printf(" Student %s not found\n", nTemp);
}
void displayFunction(void)
{
int count = 0;
if (head->next == NULL) {
printf(" No student record\n");
}
else {
printf(" NAME SCORE\n");
printf(" ---------------------------\n");
current = head->next;
while (current != NULL) {
printf(" %-20s %3d\n", current->name, current->score);
count++;
current = current->next;
}
printf(" ---------------------------\n");
printf(" Total %d record(s) found\n", count);
}
}
void insertAt(void) {
int position;
printf("\n Enter position to insert: ");
scanf("%d", &position);
if (position < 1) {
printf(" Error: Invalid position\n");
return;
}
prev = head;
current = head->next;
int count = 1;
while (current != NULL && count < position)
{
prev = current;
current = current->next;
count++;
}
if (current == NULL && count < position) {
printf(" Error: Position out of range\n");
return;
}
struct student* newNode = (struct student*)malloc(sizeof(struct student));
printf("\n Student name : ");
scanf("%s", newNode->name);
printf(" Student score: ");
scanf("%d", &newNode->score);
newNode->next = current;
prev->next = newNode;
}
void search() {
printf("\nEnter name: ");
char nTemp[20];
scanf("%s", nTemp);
current = head->next;
int count = 1;
if (current->next == NULL)
{
printf("\nElement not found\n");
}
while (current != NULL)
{
if (strcmp(current->name, nTemp) == 0)
{
printf("\nElement found at position %d\n", count);
}
current = current->next;
count++;
}
}
void reverseList() {
struct student* prev = NULL;
struct student* current = NULL;
struct student* forward = head->next;
while (forward != NULL)
{
prev = current;
current = forward;
forward = forward->next;
current->next = prev;
}
head->next = current;
}
struct student* CreateList(void) {
int num;
printf("\n Enter the number of students you want to add to the list: ");
scanf("%d", &num);
flushBuffer();
for (int i = 1; i <= num; i++) {
printf("\n=======================================");
printf("\nEnter Data for Node %d : ", i);
printf("\n Student name: ");
ptr = (struct student*)malloc(sizeof(struct student));
scanf("%s", ptr->name);
printf(" Student score: ");
scanf("%d", &ptr->score);
flushBuffer();
sortFunction();
}
printf("\nList Created");
return ptr;
}
struct student* Concat(struct student* L1) {
struct student* L2, * ptr, * temp;
printf("\n------- Insertion for List 2 ---------\n");
L2 = CreateList();
printf("\n-------- After Concatenation ---------\n");
if (L1->next != NULL)
{
if (L2->next != NULL)
{
ptr = L1->next;
while (ptr->next != NULL)
{
ptr = ptr->next;
}
ptr = L2->next;
L2 = NULL;
}
}
else
{
L1 = L2->next;
}
return L1;
}
int getLength(struct student* head) {
if (head == NULL) {
printf("Error : Invalid ListNode pointer !!!\n");
return 0;
}
int length = 0;
while (head != NULL) {
head = head->next;
length++;
}
return length;
}
struct student* kthNodeFromEnd(struct student* head, int k) {
struct student* pTemp, * kthNode;
int i;
pTemp = kthNode = head;
if (k > getLength(head)) {
printf("Error : k is greater than length of linked list\n");
return NULL;
}
for (i = 0; i < k - 1; i++) {
pTemp = pTemp->next;
}
while (pTemp->next != NULL) {
pTemp = pTemp->next;
kthNode = kthNode->next;
}
return kthNode;
}
struct student* middleNode(struct student* head) {
if (head == NULL) {
printf("Error: List is empty!\n");
return NULL;
}
struct student* slow, * fast;
slow = fast = head;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
int IsLinkedListLengthEven(struct student* listHead) {
while (listHead && listHead->next)
{
listHead = listHead->next->next;
}
if (!listHead)
{
return 0;
}
return 1;
}
void flushBuffer()
{
while (getchar() != '\n')
continue;
}
Time Complexity
Operation | Time Complexity | Worst Time Complexity | Space Complexity |
Insertion | O(1) | O(n) | O(1) |
Deletion | O(1) | O(n) | O(1) |
search | O(1) | O(n) | O(1) |
單向鏈結串列的優點:
插入和刪除時不需要移動
可以動態分配記憶體
單向鏈結串列的缺點:
查詢節點的效率低
無法查看前一個節點
需要額外的記憶體