Array、Singly Linked List

·

9 min read

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. 需要額外的記憶體