二、数据结构-线性表

线性表是一种基本的数据结构,其中的元素按照线性的方式排列,每个元素最多有一个前驱和一个后继。可以把它想象成一条链子,链子上每个环节都只与前后相邻的环节相连,没有跨度。常见的线性表包括数组和链表,其中数组是静态的线性表,链表是动态的线性表。线性表可以执行一些常见的操作,如插入、删除、查找和遍历。这些操作通常可以在常数时间内完成,因此线性表是一种高效的数据结构,被广泛应用于算法设计和实现中。

1.顺序表

顺序表是一种基于数组的线性表,它将所有元素存储在一段连续的内存空间中,并通过元素在数组中的下标来访问这些元素。

1.1优缺点

优点:

  • 随机访问效率高:由于顺序表的元素存储在一段连续的内存空间中,并通过元素在数组中的下标来访问,因此可以在常数时间O(1)内随机访问任何一个元素,访问速度非常快。
  • 空间利用率高:顺序表中的元素存储在一段连续的内存空间中,没有存储元素之间的指针,因此空间利用率比较高。
  • 可以通过简单的算术运算实现元素之间的逻辑关系,例如求后继元素和前驱元素。

缺点:

  • 插入和删除操作效率低:在进行插入和删除操作时,需要将插入或删除点之后的所有元素向后或向前移动,以保证元素在数组中的位置是连续的,这样就会导致操作的效率较低。平均时间复杂度为O(n)
  • 内存空间分配固定:由于顺序表的内存空间是一次性分配的,因此其容量是固定的,不能动态扩展或缩小,如果顺序表的元素数量超过了容量,就需要重新分配空间并进行数据搬迁,这会导致效率低下。
  • 对元素的插入和删除有限制:由于插入和删除操作效率低,不适用于需要频繁进行插入和删除操作的场景。

1.2实现

1.数据结构定义:需要定义一个结构体来表示顺序表的属性,其中包括数据存储区域、当前元素数量和容量等属性。定义时可以采用动态分配内存的方式,即先分配一定的内存空间,当元素数量超过了当前容量时,再动态地重新分配更大的内存空间,从而扩容。
2.基本操作实现:顺序表需要实现一些基本的操作,如插入、删除、查找和遍历等。
(1)插入操作:在顺序表的任意位置插入一个元素,需要先判断是否需要扩容,然后将插入点之后的所有元素向后移动一个位置,最后将新元素插入到指定位置。
(2)删除操作:在顺序表的任意位置删除一个元素,需要将删除点之后的所有元素向前移动一个位置,最后将数组的长度减1。
(3)查找操作:顺序表可以通过下标进行随机访问,因此查找操作的时间复杂度是O(1)。
(4)遍历操作:可以通过循环遍历顺序表的所有元素,实现顺序表的遍历操作。

  • C语言实现:
#include <stdio.h>
#include <stdlib.h>

typedef int Item;
typedef enum {
    
    
    false = 0, true = 1
} bool;

struct List {
    
    
    Item *array;
    int volume;
    int size;
};

bool initList(struct List *list) {
    
    
    list->array = (Item *) calloc(10, sizeof(Item));
    if (list->array == NULL) {
    
    
        printf("内存不足!列表初始化失败!");
        return false;
    } else {
    
    
        list->volume = 10;
        list->size = 0;
        return true;
    }
}

bool insertList(struct List *list, Item item, int index) {
    
    
    if (index < 0 || index > list->size) {
    
    
        printf("索引越界,插入失败!");
        return false;
    }
    if (list->size == list->volume) {
    
    
        list->volume = list->volume + (list->volume >> 1);
        list->array = (Item *) realloc(list->array, list->volume * sizeof(Item));
        if (list->array == NULL) {
    
    
            printf("内存不足!插入失败!");
            return false;
        }
    }
    for (int i = list->size; i > index; i--) {
    
    
        list->array[i] = list->array[i - 1];
    }
    list->array[index] = item;
    list->size++;
    return true;
}

bool removeList(struct List *list, int index) {
    
    
    if (index < 0 || index > list->size) {
    
    
        printf("索引越界,删除失败!");
        return false;
    } else {
    
    
        for (int i = index; i < list->size - 1; ++i) {
    
    
            list->array[i] = list->array[i + 1];
        }
        if (((float)list->volume/(float)list->size)>1.5f && list->capacity > 10){
    
    
            list->volume = list->volume - (list->volume >> 1);
            if (list->volume < 10) {
    
    
            list->volume = 10;
        	}
            list->array = (Item *) realloc(list->array, list->volume * sizeof(Item));
        }
        list->size--;
        return true;
    }

}

int sizeList(struct List *list) {
    
    
    return list->size;
}

Item getList(struct List *list, int index) {
    
    
    if (index < 0 || index > list->size) {
    
    
        printf("索引越界,获取元素失败!");
        exit(1);
    } else {
    
    
        return list->array[index];
    }
}

int indexList(struct List *list, Item item) {
    
    
    for (int i = 0; i < list->size; i++) {
    
    
        if (list->array[i] == item) return i;
    }
    return -1;
}

void printList(struct List *list) {
    
    
    for (int i = 0; i < list->size; ++i) {
    
    
        printf("%d ", list->array[i]);
    }
    printf("\n");
}

int main() {
    
    
    struct List list;
    initList(&list);
    for (int i = 20; i >= 0; i--) {
    
    
        insertList(&list, 1 * i, 0);
    }
    printList(&list);
    removeList(&list, 5);
    printList(&list);
    insertList(&list, 29, 19);
    printList(&list);
    printf("列表的长度为:%d\n", sizeList(&list));
    printf("列表索引为【%d】的元素是:%d\n", 5, getList(&list, 5));
    printf("元素【%d】在列表中的索引为:%d\n", 6, indexList(&list, 6));
    return 0;
}

输出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 29 20
列表的长度为:21
列表索引为【5】的元素是:6
元素【6】在列表中的索引为:5
  • Python实现:
# -*- coding: utf-8 -*- 
# @Time : 2023/3/9 15:08 
# @Author : yanhucheng 
# @File : 顺序表.py

class List:
    def __init__(self):
        self._array = []
        self._size = 0

    def insert(self, item, index: int) -> bool:
        if index < 0 or index > self._size:
            print("索引越界,插入失败!")
            return False
        else:
            self._array.append(item)
            for i in range(self._size, index, -1):
                self._array[i], self._array[i - 1] = self._array[i - 1], self._array[i]
            self._size += 1
            return True

    def remove(self, index: int) -> bool:
        if index < 0 or index > self._size:
            print("索引越界,删除失败!")
            return False
        else:
            for i in range(index, self._size - 1):
                self._array[i], self._array[i + 1] = self._array[i + 1], self._array[i]
            self._array.pop()
            self._size -= 1
            return True

    def get(self, index):
        if index < 0 or index > self._size:
            print("索引越界,获取元素失败!")
            return None
        else:
            return self._array[index]

    def find(self, item) -> int:
        for i in range(0, self._size):
            if self._array[i] == item:
                return i
        else:
            return -1

    def print(self):
        for item in self._array:
            print("%s" % item, end=" ")
        print()

    @property
    def length(self):
        return self._size


if __name__ == '__main__':
    my_list = List()
    for i in range(21):
        my_list.insert(i, my_list.length)
    my_list.print()
    my_list.remove(5)
    my_list.print()
    my_list.insert(29, 19)
    my_list.print()
    print("列表的长度为:%d" % my_list.length)
    print("列表索引为【%d】的元素是:%d" % (5, my_list.get(5)))
    print("元素【%d】在列表中的索引为:%d" % (6, my_list.find(6)))

输出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 29 20 
列表的长度为:21
列表索引为【5】的元素是:6
元素【6】在列表中的索引为:5

2.链表

链表是指在内存中非连续、非顺序存储的一种数据结构,它的逻辑顺序是由链表的指针连接次序实现的。链表由一系列节点组成,每个节点包含有节点数据以及相邻节点的指针信息。

2.1单向链表

单向链表是最基本的链表结构,它的节点只包含两部分内容:

  • 存储的数据
  • 后继节点的指针
head
节点1
节点2
节点3
NULL

上图是一个包含3个节点的单向链表示意图,每个链表至少要包含一个头指针和一个尾指针,一般头指针不作为数据节点。下面我们在单向链表的节点1和节点2之间插入一个节点4,我们要实现的结果如下图:

head
节点1
节点4
节点2
节点3
NULL

操作步骤:

  • 1.首先我们要找到节点1,然后根据节点1的后继节点指针找到节点2的地址,先将节点4的后继节点指针指向节点2,如下图所示:
head
节点1
节点2
节点3
NULL
节点4
  • 2.然后再将节点1后继节点指针指向节点4的地址,这样节点4就成功插入链表中了。
head
节点1
节点4
节点2
节点3
NULL

2.1.1 单向链表的基本操作

  • (1)插入操作:在链表中插入一个新节点,需要将新节点插入到原有节点的前面或后面,并更新前后节点的指针。
  • (2)删除操作:从链表中删除一个节点,需要将该节点从链表中断开,并更新其前后节点的指针。
  • (3)查找操作:在链表中查找某个节点,可以使用一个循环实现,从头节点开始遍历每个节点,直到找到目标节点或遍历到链表结尾。
  • (4)遍历操作:按照顺序遍历链表中的所有节点,可以使用一个循环实现。

单向链表的优点是插入和删除操作比较高效,不需要像顺序表一样需要移动大量元素。缺点是访问任意位置的元素比较困难,需要从头节点开始遍历整个链表,而且由于节点需要存储指针,相对于顺序表会占用更多的内存空间。

2.1.2 单向链表的实现

  • C语言实现:
#include <stdio.h>
#include <stdlib.h>

typedef int Item;
typedef unsigned int Index;
typedef enum {
    
    
    false = 0,
    true = 1
} bool;

// 定义单链表节点的结构体
struct ListNode {
    
    
    Item item;
    struct ListNode *next;
};

typedef struct ListNode *Node; // 为结构体指针起别名

// 分配节点内存
Node newNode() {
    
    
    Node node = (Node) malloc(sizeof(struct ListNode));
    if (node == NULL) {
    
    
        printf("内存不足!\n");
        return NULL;
    } else {
    
    
        return node;
    }
}

// 释放节点内存
void freeNode(Node node) {
    
    
    free(node); // 释放节点内存空间
    node = NULL; // 释放节点指针变量内存空间
}

// 遍历单链表
void printNodeList(Node head) {
    
    
    Node temp = head;
    while (temp->next != NULL) {
    
    
        temp = temp->next;
        printf("%d ", temp->item);
    }
    printf("\n");
}

// 初始化单链表
Node initListNode() {
    
    
    Node head = newNode();
    if (head != NULL) {
    
    
        head->next = NULL;
    }
    return head;
}

// 单链表插入节点操作
bool insertNode(Node head, Item item, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; ++i) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,插入失败!\n");
            return false;
        }
    }
    Node node = newNode();
    node->item = item;
    node->next = temp->next;
    temp->next = node;
    return true;
}

// 单链表节点删除操作
bool removeNode(Node head, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; i++) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,删除失败!\n");
            return false;
        }
    }
    if(temp->next == NULL){
    
    
        printf("索引越界,删除失败!\n");
        return false;
    }
    Node rm_node = temp->next;
    temp->next = temp->next->next;
    freeNode(rm_node);
    return true;
}

// 单链表节点获取
Item getNode(Node head, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; ++i) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,获取失败!\n");
            return EOF;
        }
    }
    if(temp->next == NULL){
    
    
        printf("索引越界,获取失败!\n");
        return EOF;
    }
    return temp->next->item;
}

// 单链表节点索引查找
Index findNode(Node head, Item item) {
    
    
    Node temp = head->next;
    for (int i = 0; temp != NULL; i++) {
    
    
        if (temp->item == item)return i;
        temp = temp->next;
    }
    return -1;
}

// 获取单链表的长度
int getLength(Node head){
    
    
    Node temp = head;
    int length = 0;
    while(temp->next != NULL){
    
    
        temp = temp->next;
        length++;
    }
    return length;
}


int main() {
    
    
    Node single_list = initListNode();
    for (int i = 0; i < 21; ++i) {
    
    
        insertNode(single_list, i, i);
    }
    printNodeList(single_list);
    removeNode(single_list, 5);
    printNodeList(single_list);
    printf("索引为%d的节点元素值为:%d\n", 5, getNode(single_list, 5));
    printf("值为%d的节点元素索引为:%d\n", 6, findNode(single_list, 6));
    printf("单链表长度为:%d\n", getLength(single_list));
    return 0;
}

输出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
索引为5的节点元素值为:6
值为6的节点元素索引为:5
单链表长度为:20
  • Python实现:
# -*- coding: utf-8 -*- 
# @Time : 2023/3/10 14:00 
# @Author : yanhucheng 
# @File : 单链表.py

class Node:
    def __init__(self):
        self.item = None
        self.next = None


class SingList:
    def __init__(self):
        self._head = Node()

    def print(self):
        """输出单链表中的元素"""
        temp = self._head.next
        while temp is not None:
            print(temp.item, end=" ")
            temp = temp.next
        print()

    def insert(self, item, index=0) -> bool:
        """
        :param item: 插入元素
        :param index: 插入索引
        :return:
        """
        temp = self._head
        i = 0
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,插入失败!")
                return False
            else:
                i += 1
        else:
            node = Node()
            node.item = item
            node.next = temp.next
            temp.next = node
            return True

    def remove(self, index) -> bool:
        """删除节点
        :param index: 删除的索引位置
        :return:
        """
        temp = self._head
        i = 0
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,删除失败!")
                return False
            else:
                i += 1
        else:
            if temp.next is None:
                print("索引越界,删除失败!")
                return False
            temp.next = temp.next.next
            return True

    def get(self, index):
        """获取指定索引位置的值

        :param index:
        :return:
        """
        i = 0
        temp = self._head
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,获取失败!")
                return -1
            else:
                i += 1
        if temp.next is None:
            print("索引越界,获取失败!")
            return -1
        else:
            return temp.next.item

    def find(self, item):
        """查找元素所在索引

        :param item:
        :return:
        """
        i = 0
        temp = self._head
        while temp.next is not None:
            temp = temp.next
            if temp.item == item:
                return i
            else:
                i += 1
        else:
            return -1

    @property
    def length(self):
        length = 0
        temp = self._head
        while temp.next is not None:
            temp = temp.next
            length += 1
        return length


if __name__ == '__main__':
    single_list = SingList()
    for i in range(21):
        single_list.insert(i, i)
    single_list.print()
    single_list.remove(5)
    single_list.print()
    print("索引为%d的节点元素值为:%d" % (5, single_list.get(5)))
    print("值为%d的节点元素索引为:%d" % (6, single_list.find(6)))
    print("单链表长度为:%d" % single_list.length)

输出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
索引为5的节点元素值为:6
值为6的节点元素索引为:5
单链表长度为:20

2.2双向链表

双向链表是在单向链表基础之上添加了一个前向指针,除此之外基本相同:

  • 节点数据
  • 后继节点指针
  • 前驱节点指针
head
节点1
节点2
节点3
NULL

双向链表与单向链表操作也基本相同,区别在于插入和删除操作时需要考虑前向指针的指向
下面给出双向链表的实现:

  • C语言实现:
#include <stdio.h>
#include <stdlib.h>

typedef int Item;
typedef unsigned int Index;
typedef enum {
    
    
    false = 0,
    true = 1
} bool;

// 定义双向链表节点的结构体
struct ListNode {
    
    
    Item item;
    struct ListNode *next;
    struct ListNode *prev;
};

typedef struct ListNode *Node; // 为结构体指针起别名

// 分配节点内存
Node newNode() {
    
    
    Node node = (Node) malloc(sizeof(struct ListNode));
    if (node == NULL) {
    
    
        printf("内存不足!\n");
        return NULL;
    } else {
    
    
        return node;
    }
}

// 释放节点内存
void freeNode(Node node) {
    
    
    free(node); // 释放节点内存空间
    node = NULL; // 释放节点指针变量内存空间
}

// 遍历双向链表
void printNodeList(Node head) {
    
    
    Node temp = head;
    printf("正向遍历:");
    while (temp->next != NULL) {
    
    
        temp = temp->next;
        printf("%d ", temp->item);
    }
    printf("\n反向遍历:");
    while (temp->prev != NULL) {
    
    
        printf("%d ", temp->item);
        temp = temp->prev;
    }
    printf("\n");
}

// 初始化双向链表
Node initListNode() {
    
    
    Node head = newNode();
    if (head != NULL) {
    
    
        head->next = NULL;
        head->prev = NULL;
    }
    return head;
}

// 双向链表插入节点操作
bool insertNode(Node head, Item item, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; ++i) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,插入失败!\n");
            return false;
        }
    }
    Node node = newNode();
    node->item = item;
    node->next = temp->next;
    if (temp->next != NULL){
    
    
        temp->next->prev = node;
    }
    node->prev = temp;
    temp->next = node;
    return true;
}

// 双向链表节点删除操作
bool removeNode(Node head, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; i++) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,删除失败!\n");
            return false;
        }
    }
    if(temp->next == NULL){
    
    
        printf("索引越界,删除失败!\n");
        return false;
    }
    Node rm_node = temp->next;
    temp->next = rm_node->next;
    if (rm_node->next != NULL){
    
    
        rm_node->next->prev = temp;
    }
    freeNode(rm_node);
    return true;
}

// 双向链表节点获取
Item getNode(Node head, Index index) {
    
    
    Node temp = head;
    for (int i = 0; i < index; ++i) {
    
    
        temp = temp->next;
        if (temp == NULL) {
    
    
            printf("索引越界,获取失败!\n");
            return EOF;
        }
    }
    if(temp->next == NULL){
    
    
        printf("索引越界,获取失败!\n");
        return EOF;
    }
    return temp->next->item;
}

// 双向链表节点索引查找
Index findNode(Node head, Item item) {
    
    
    Node temp = head->next;
    for (int i = 0; temp != NULL; i++) {
    
    
        if (temp->item == item)return i;
        temp = temp->next;
    }
    return -1;
}

// 获取双向链表的长度
int getLength(Node head){
    
    
    Node temp = head;
    int length = 0;
    while(temp->next != NULL){
    
    
        temp = temp->next;
        length++;
    }
    return length;
}


int main() {
    
    
    Node single_list = initListNode();
    for (int i = 0; i < 21; ++i) {
    
    
        insertNode(single_list, i*10, i);
    }
    printNodeList(single_list);
    removeNode(single_list, 1);
    printNodeList(single_list);
    printf("索引为%d的节点元素值为:%d\n", 10, getNode(single_list, 10));
    printf("值为%d的节点元素索引为:%d\n", 60, findNode(single_list, 60));
    printf("双向链表长度为:%d\n", getLength(single_list));
    return 0;
}

输出:

正向遍历:0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200
反向遍历:200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 10 0
正向遍历:0 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200
反向遍历:200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 0
索引为10的节点元素值为:110
值为60的节点元素索引为:5
双向链表长度为:20
  • Python实现:
# -*- coding: utf-8 -*- 
# @Time : 2023/3/13 10:46 
# @Author : yanhucheng 
# @File : 双向链表.py
class Node:
    def __init__(self):
        self.item = None
        self.next = None
        self.prev = None


class SingList:
    def __init__(self):
        self._head = Node()

    def print(self):
        """输出双向链表中的元素"""
        temp = self._head
        print("正向遍历:", end="")
        while temp.next is not None:
            temp = temp.next
            print(temp.item, end=" ")
        print("\n反向遍历:", end="")
        while temp.prev is not None:
            print(temp.item, end=" ")
            temp = temp.prev
        print()

    def insert(self, item, index=0) -> bool:
        """
        :param item: 插入元素
        :param index: 插入索引
        :return:
        """
        temp = self._head
        i = 0
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,插入失败!")
                return False
            else:
                i += 1
        else:
            node = Node()
            node.item = item
            if temp.next is not None:
                temp.next.prev = node
            node.next = temp.next
            node.prev = temp
            temp.next = node
            return True

    def remove(self, index) -> bool:
        """删除节点
        :param index: 删除的索引位置
        :return:
        """
        temp = self._head
        i = 0
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,删除失败!")
                return False
            else:
                i += 1
        else:
            if temp.next is None:
                print("索引越界,删除失败!")
                return False
            if temp.next.next is not None:
                temp.next.next.prev = temp
            temp.next = temp.next.next
            return True

    def get(self, index):
        """获取指定索引位置的值

        :param index:
        :return:
        """
        i = 0
        temp = self._head
        while i < index:
            temp = temp.next
            if temp is None:
                print("索引越界,获取失败!")
                return -1
            else:
                i += 1
        if temp.next is None:
            print("索引越界,获取失败!")
            return -1
        else:
            return temp.next.item

    def find(self, item):
        """查找元素所在索引

        :param item:
        :return:
        """
        i = 0
        temp = self._head
        while temp.next is not None:
            temp = temp.next
            if temp.item == item:
                return i
            else:
                i += 1
        else:
            return -1

    @property
    def length(self):
        length = 0
        temp = self._head
        while temp.next is not None:
            temp = temp.next
            length += 1
        return length


if __name__ == '__main__':
    single_list = SingList()
    for i in range(21):
        single_list.insert(i*10, i)
    single_list.print()
    single_list.remove(1)
    single_list.print()
    print("索引为%d的节点元素值为:%d" % (10, single_list.get(10)))
    print("值为%d的节点元素索引为:%d" % (60, single_list.find(60)))
    print("双向链表长度为:%d" % single_list.length)

输出:

正向遍历:0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 
反向遍历:200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 10 0 
正向遍历:0 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 
反向遍历:200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 0 
索引为10的节点元素值为:110
值为60的节点元素索引为:5
双向链表长度为:20

2.3循环链表

循环链表是在前面的单向链表和双向链表基础上,将头节点和尾结点连接起来构成的,因为我们的head是不存放数据的节点,我们可以把它看做链表整体,不算做链表的节点

  • 单向循环链表:
head
节点1
节点2
节点3
  • 双向循环链表:
head
节点1
节点2
节点3
节点4

循环链表的实现跟上面的基本相同,这里不再赘述。

3.栈

栈是先进后出的一种列表,他的入栈出栈操作只能在同一端,可以把栈想象成一个杯子,数据先放进去的肯定在杯子底部,最先取出来的肯定是杯子最上层的东西。
栈结构示意图
我们从上面的栈结构取出数据时,最先出的肯定是5,然后才是4、3、2、1、0。

栈结构的实现可以使用顺序表也可以使用链表,下面我们用C语言来实现顺序表栈结构,用python来实现链表式栈结构,以下只给出入栈和出栈操作,一般还有判断栈结构是否为空、清空栈操作,这里就不做演示了。

  • 顺序表实现栈结构:
    顺序表实现栈结构,主要是记录栈顶的位置,每入栈一个元素就将当前元素所在位置记录下来,相当于是记录了数组的最后一个元素的索引。出栈时将索引往前移。判断栈是否为空栈就只需要判断当前位置是否在-1就可以了。
#include <stdio.h>
#include <stdlib.h>

typedef int Item;
typedef enum {
    
    
    false = 0,
    true = 1
} bool;

// 顺序表实现栈
struct SequentialList {
    
    
    Item *array; // 顺序表数组
    int capacity; // 数组容量
    int p; // 栈顶当前位置的指针
};

typedef struct SequentialList *SeqList;

// 初始化顺序表栈
SeqList initSeqStack() {
    
    
    static struct SequentialList temp_list;
    SeqList list = &temp_list;
    list->array = (Item *) calloc(10, sizeof(Item));
    if (list->array == NULL) {
    
    
        printf("内存不足!列表初始化失败!");
        return NULL;
    } else {
    
    
        list->capacity = 10;
        list->p = -1;
        return list;
    }

}

// 顺序表栈入栈操作
bool pushSeqStack(SeqList list, Item item) {
    
    
    if (list->p == list->capacity - 1) {
    
    
        int new_capacity = list->capacity + (list->capacity >> 1);
        void *temp = realloc(list->array, sizeof(Item) * (new_capacity));
        if (temp == NULL) {
    
    
            printf("内存不足!入栈失败!");
            return false;
        } else {
    
    
            list->capacity = new_capacity;
        }
    }
    list->array[++list->p] = item;
    return true;

}

// 顺序表栈出栈操作
Item popSeqStack(SeqList list) {
    
    
    if (list->p == -1) {
    
    
        printf("空栈不能出栈!\n");
        return EOF;
    }
    if (((float) list->capacity / ((float) list->p + 1)) > 1.5f && list->capacity > 10) {
    
    
        list->capacity = list->capacity - (list->capacity >> 1);
        if (list->capacity < 10) {
    
    
            list->capacity = 10;
        }
        list->array = (Item *) realloc(list->array, list->capacity * sizeof(Item));
    }
    Item item = list->array[list->p--];
    return item;
}

void printSeqStack(SeqList list) {
    
    
    printf("顺序表栈空间:栈顶 >>");
    for (int i = list->p; i > 0; i--) {
    
    
        printf(" %d >>", list->array[i]);
    }
    if (list->p != -1) {
    
    
        printf(" %d", list->array[0]);
    } else {
    
    
        printf(" NULL");
    }
    printf(" >> 栈底\n");
}

int main() {
    
    
    SeqList seq_stack = initSeqStack();
    for (int i = 0; i < 12; ++i) {
    
    
        pushSeqStack(seq_stack, i);
    }
    printSeqStack(seq_stack);
    for (int i = 1; i <= 3; ++i) {
    
    
        printf("第%d次出栈元素:%d\n", i, popSeqStack(seq_stack));
    }
    printSeqStack(seq_stack);
    return 0;
}

输出:

顺序表栈空间:栈顶 >> 11 >> 10 >> 9 >> 8 >> 7 >> 6 >> 5 >> 4 >> 3 >> 2 >> 1 >> 0 >> 栈底
第1次出栈元素:11
第2次出栈元素:10
第3次出栈元素:9
顺序表栈空间:栈顶 >> 8 >> 7 >> 6 >> 5 >> 4 >> 3 >> 2 >> 1 >> 0 >> 栈底
  • 链表实现栈结构:
    链表实现栈主要使用头插法,在头结点进行插入和删除操作
# -*- coding: utf-8 -*- 
# @Time : 2023/3/14 16:27 
# @Author : yanhucheng 
# @File : 栈.py

# 链表实现栈
class Node:
    def __init__(self):
        self.item = None
        self.next = None


class LinkStack:
    def __init__(self):
        self._head = Node()

    def push(self, item):
        node = Node()
        node.item = item
        node.next = self._head.next
        self._head.next = node

    def pop(self):
        if self._head.next is not None:
            temp = self._head.next
            self._head.next = temp.next
            return temp.item
        else:
            print("空栈不能出栈!")
            return None

    def print(self):
        print("链表栈空间:栈顶 >> ", end="")
        temp = self._head
        while temp.next is not None:
            temp = temp.next
            print(temp.item, end=" >> ")
        if self._head == temp:  # 当栈为空栈时
            print("NULL", end=" >> ")
        print("栈底")


if __name__ == '__main__':
    link_stack = LinkStack()
    for i in range(12):
        link_stack.push(i)
    link_stack.print()
    for i in range(3):
        print(f"第{
      
      i + 1}次出栈元素:{
      
      link_stack.pop()}")
    link_stack.print()

输出:

链表栈空间:栈顶 >> 11 >> 10 >> 9 >> 8 >> 7 >> 6 >> 5 >> 4 >> 3 >> 2 >> 1 >> 0 >> 栈底
第1次出栈元素:11
第2次出栈元素:10
第3次出栈元素:9
链表栈空间:栈顶 >> 8 >> 7 >> 6 >> 5 >> 4 >> 3 >> 2 >> 1 >> 0 >> 栈底

4.队列

队列(Queue)是一种线性数据结构,可以看作特殊的线性表,在队列中插入元素的操作称为进队(Enqueue),删除元素的操作称为出队(Dequeue)。队列的特点是先进先出(FIFO, First-In-First-Out)。在队列中,新元素插入的一端称为队尾,元素删除的一端称为队头。

  • 队头(front):队列的数据出口
  • 队尾(rear):队列的数据入口
    队列示意图
    队列也可以使用顺序表和链表两种方式进行实现,下面我将使用C语言实现基于顺序表的队列基本操作,然后用python实现基于链表的队列基本操作。
  • C语言实现:
    下面用C语言实现一个基于顺序表的队列,队列为固定长度的顺序表,当队尾指针走到顺序表尾时,将指针指回表头构成循环使用。队头指针用来取数据,队尾指针用来放数据,当队尾的下下个指针指向队头指针说明队列已满。(队头指针与队尾指针之间需要隔一个空的数据用来判断容量是否已满)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int Item;

struct ListQueue {
    
    
    Item *array;
    int capacity;
    int front; // 队头指针
    int rear; // 队尾指针
};

typedef struct ListQueue *Queue;

bool initQueue(Queue queue, int capacity) {
    
    
    queue->array = (Item *) malloc(sizeof(Item) * (capacity + 1));
    if (queue->array == NULL) {
    
    
        printf("内存不足,队列初始化失败!");
        return false;
    }
    queue->capacity = capacity + 1;
    queue->front = queue->rear = 0;
    return true;
}

bool isEmptyQueue(Queue queue) {
    
    
    if (queue->front == queue->rear) {
    
    
        printf("队列为空!\n");
        return true;
    }
    return false;
}

bool putQueue(Queue queue, Item item) {
    
    
    if ((queue->rear + 1) % queue->capacity == queue->front) {
    
    
        printf("队列已满!【%d】入队失败!\n", item);
        return false;
    }
    queue->array[queue->rear] = item;
    queue->rear = (queue->rear + 1) % queue->capacity;
    return true;
}

Item getQueue(Queue queue) {
    
    
    if (isEmptyQueue(queue)) return EOF;
    Item item = queue->array[queue->front];
    queue->front = (queue->front + 1) % queue->capacity;
    return item;
}

void printQueue(Queue queue) {
    
    
    if (!isEmptyQueue(queue)) {
    
    
        printf("<<<");
        int temp = queue->front;
        while (temp != queue->rear) {
    
    
            printf(" %d ", queue->array[temp]);
            temp = (temp + 1) % queue->capacity;
        }
        printf("<<<\n");
    }
}

void clearQueue(Queue queue) {
    
    
    queue->front = queue->rear = 0;
}

int main() {
    
    
    struct ListQueue queue;
    initQueue(&queue, 10);
    for (int i = 0; i < 11; ++i) {
    
    
        putQueue(&queue, i);
    }
    printQueue(&queue);
    for (int i = 0; i < 3; ++i) {
    
    
        printf("元素【%d】出队列\n", getQueue(&queue));
    }
    printQueue(&queue);
    // 再次入队3个元素10、11、12
    for (int i = 10; i < 13; ++i) {
    
    
        putQueue(&queue, i);
    }
    printQueue(&queue);
    clearQueue(&queue); // 清空队列
    printQueue(&queue);
    return 0;
}

输出:

<<< 0  1  2  3  4  5  6  7  8  9 <<<
元素【0】出队列
元素【1】出队列
元素【2】出队列
<<< 3  4  5  6  7  8  9 <<<
<<< 3  4  5  6  7  8  9  10  11  12 <<<
队列为空!
  • Python实现:
    下面使用python实现一个基于链表的队列,主要的要点是队头指针和队尾指针,队头指针指向的是头节点,队尾指针指向的是尾结点,入队时利用队尾指针做插入操作,出队时利用队头指针进行删除操作,当队头指针回到头节点表示队列为空,值得注意的是:清空队列时,我使用一个临时变量存储头节点的下一个节点的指针,然后另起一个线程做内存回收,如果直接将头指针指向None,则会造成链表中间节点索引丢失,内存也没有释放,有内存溢出的风险
# -*- coding: utf-8 -*- 
# @Time : 2023/3/15 15:05 
# @Author : yanhucheng 
# @File : 队列.py
"""链表实现队列"""
import threading


class Node:
    def __init__(self):
        self.item = None
        self.next = None


class LinkQueue:
    def __init__(self):
        self._front = Node()
        self._rear = self._front

    def is_empty(self):
        if self._front == self._rear:
            return True
        else:
            return False

    def put(self, item):
        node = Node()
        node.item = item
        node.next = self._rear.next
        self._rear.next = node
        self._rear = node

    def get(self):
        if not self.is_empty():
            item = self._front.next.item
            self._front.next = self._front.next.next
            if self._front.next is None:
                self._front = self._rear
            return item

    def clear(self):
        temp = self._front.next
        self._front.next = None
        self._rear = self._front
        t = threading.Thread(target=self.__free, args=(temp,))
        t.setDaemon(True)
        t.start()

    def print(self):
        if self.is_empty():
            print("队列为空!")
        else:
            temp = self._front
            print("<<<", end="")
            while temp.next is not None:
                temp = temp.next
                print(f" {
      
      temp.item} ", end="")
            print("<<<")

    def __free(self, head: Node):
        while head is not None:
            temp = head.next
            del head
            head = temp


if __name__ == '__main__':
    lq = LinkQueue()
    for i in range(10):
        lq.put(i)
    lq.print()
    for i in range(3):
        print("元素【{}】出队列".format(lq.get()))
    lq.print()
    for i in range(10, 13):
        lq.put(i)
    lq.print()
    lq.clear()
    lq.print()

输出:

<<< 0  1  2  3  4  5  6  7  8  9 <<<
元素【0】出队列
元素【1】出队列
元素【2】出队列
<<< 3  4  5  6  7  8  9 <<<
<<< 3  4  5  6  7  8  9  10  11  12 <<<
队列为空!

猜你喜欢

转载自blog.csdn.net/m0_56963884/article/details/129424722
今日推荐