Skip to main content

Command Palette

Search for a command to run...

Array、Singly Linked List

Updated
•9 min read•View as Markdown
Array、Singly Linked List
K

コツコツやる

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

OPERATIONWORST CASEAVERAGE CASEBEST CASE
ResizeO(N)O(N)O(N)
AddO(1)O(1)O(1)
Add at an indexO(N)O(N)O(1)
DeleteO(1)O(1)O(1)
Delete at an indexO(N)O(N)O(1)

單向鏈結串列

結構定義

加入動作

  1. 加入於串列的前端

  2. 加入於串列的後端

  3. 隨機加入某一節點

刪除動作

  1. 刪除串列前端的節點

  2. 刪除串列的尾端節點

  3. 隨機刪除某一節點

將兩串列相連接

將串列反轉

計算串列的長度

完整的程式碼:

#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

OperationTime ComplexityWorst Time ComplexitySpace Complexity
InsertionO(1)O(n)O(1)
DeletionO(1)O(n)O(1)
searchO(1)O(n)O(1)

單向鏈結串列的優點:

  1. 插入和刪除時不需要移動

  2. 可以動態分配記憶體

單向鏈結串列的缺點:

  1. 查詢節點的效率低

  2. 無法查看前一個節點

  3. 需要額外的記憶體

More from this blog

Binary Tree

樹狀結構的一些專有名詞 一般的樹狀結構擁有以下的特徵: 節點:代表某項資料 根結點:沒有父節點的節點,一顆樹也只能有一個根結點 父節點、子節點:若某個節點連接到下面一個節點,則該節點為父節點,而下面的節點稱為子節點 祖先節點、子孫節點:若某個節點有一條路徑可以通往另一個節點,那就代表該節點為祖先節點 兄弟節點:當它們都是同一個父節點的子節點時則該子節點們互為兄弟節點 樹葉節點:分支度為0的節點 分支度:一個節點有幾個子節點稱為分支度 高度:某節點到距離最遠的樹葉節點的最長路徑...

Jun 12, 20235 min read

Multiple Stacks

Introduction 所謂的多個堆疊,是指在一個陣列裡實現K個堆疊,如下方圖片裡,3個堆疊要平分陣列裡的位址 要實現多個堆疊有兩個方法可以實現 Simple method:將陣列劃分為 n/k Efficient method:節省空間 Implementation Simple method 結構定義 push() 加入時要先判斷該堆疊是否已經到下一個堆疊的bottom了 pop() 刪除時只要判斷同一個堆疊是否相同就好,然後再把該堆疊的top遞減 完整的程式碼 #incl...

May 27, 20235 min read
Multiple Stacks

Doubly Circular Linked List

Introduction 雙向鏈結串列與單向鏈結串列相比,雙向鏈結串列多了一個指向前一個節點的指標 並且最後一個節點會重新指回head節點,head節點也會指向最後一個節點 Methods 以下為雙向鏈結串列常見的操作 Insertion:加入節點 Deletion:刪除節點 Search:查詢該節點是否存在於雙向鏈結串列 Reverse:反轉雙向鏈結串列 Time Complexity OperationTime ComplexityWorst Time ComplexityS...

May 14, 20235 min read
Doubly Circular Linked List

Data Structure: Queue

Introduction 佇列是一種先進先出的一種資料結構,類似於堆疊,但差別在於佇列刪除的元素是最早加入的元素 但是會發生陣列的前面還有空間,但因為rear已經大於等於n - 1,所以會產生佇列已滿的訊息,因此佇列常常以環狀佇列的方式來表示 Methods Enqueue:加入資料到佇列裡 Dequeue:刪除front變數所指的值 isEmpty:檢查佇列是否是空的 Peek:取得front變數所指的值 Time Complexity OperationTime Comple...

May 10, 20234 min read

kiniku

6 posts