数据结构专题 - 线性表

   线性表是数据结构中最基础、最常用的数据结构之一,它在实际应用中非常广泛。无论是操作系统中的内存管理,还是数据库中的索引结构,线性表都扮演着重要角色。

一、线性表的概念与抽象数据类型

1.1 线性表的逻辑结构

线性表是由nn ≥ 0)个数据元素组成的有限序列。它的逻辑结构具有以下特点:

  • 每个元素在线性表中都有一个唯一的前驱(除了第一个元素)

  • 每个元素在线性表中都有一个唯一的后继(除了最后一个元素)

  • 元素之间是线性关系(即一对一的关系)

例如,一个学生的成绩列表 [90, 85, 78, 92, 88] 就是一个线性表。

1.2 线性表的抽象数据类型(ADT)

线性表的抽象数据类型定义了它的一组操作接口,而不关心具体的实现细节。以下是线性表的主要操作:

  • 初始化:创建一个空的线性表

  • 插入:在指定位置插入一个新元素

  • 删除:删除指定位置的元素

  • 查找:查找某个元素的位置

  • 遍历:依次访问线性表中的每个元素

  • 清空:清空线性表中的所有元素

1.3 类型定义

线性表中的元素可以是任意类型,例如整数、字符串、对象等。在实际应用中,线性表的元素类型通常根据具体需求定义。

二、线性表的顺序存储

2.1 顺序存储结构

顺序存储是用一段连续的内存空间来存储线性表的元素。通常用数组来实现顺序存储。顺序存储的特点:

  • 优点:支持随机访问,访问速度快

  • 缺点插入和删除操作需要移动大量元素

2.2 顺序存储结构上的基本运算

插入操作

插入操作需要将插入位置之后的所有元素向后移动一位,为新元素腾出空间

初始状态:[a0, a1, a2, a3, a4]
插入位置:2,插入元素:x
操作后:[a0, a1, x, a2, a3, a4]
删除操作

删除操作需要将删除位置之后的所有元素向前移动一位,填补空缺。

初始状态:[a0, a1, a2, a3, a4]
删除位置:2
操作后:[a0, a1, a3, a4]

2.3 顺序存储的代码实现

C语言实现
​
#include 
#include 

#define MAXSIZE 100

typedef struct {
    int data[MAXSIZE];
    int length;
} SeqList;

// 初始化
void InitList(SeqList *L) {
    L->length = 0;
}

// 插入
int Insert(SeqList *L, int pos, int value) {
    if (pos < 1 || pos > L->length + 1 || L->length >= MAXSIZE) { 
        return 0;
    } 
    int i; // 声明变量
    for (i = L->length - 1; i >= pos - 1; i--) { 
        L->data[i + 1] = L->data[i];
    } 
    L->data[pos - 1] = value;
    L->length++;
    return 1;     
}

// 删除
int Delete(SeqList *L, int pos, int *value) {
    if (pos < 1 || pos > L->length) { 
        return 0;
    } 
    *value = L->data[pos - 1];
    int i; // 声明变量
    for (i = pos; i < L->length; i++) { 
        L->data[i - 1] = L->data[i];
    } 
    L->length--;
    return 1;
}

// 查找
int Locate(SeqList L, int value) {
    int i; // 声明变量
    for (i = 0; i < L.length; i++) { 
        if (L.data[i] == value) { 
            return i + 1;
        }
    }		 
    return 0;
}

int main() {
    SeqList L;
    InitList(&L);
    
    Insert(&L, 1, 10);
    Insert(&L, 2, 20);
    Insert(&L, 3, 30);
    
    int value;
    Delete(&L, 2, &value);
    printf("Deleted value: %d\n", value);
    
    int pos = Locate(L, 30);
    printf("Position of 30: %d\n", pos);
    
    return 0;
}

​
Python实现
​
class SeqList:
    def __init__(self, size=100):
        self.data = [None] * size
        self.length = 0
        self.size = size

    def insert(self, pos, value):
        if pos < 1 or pos > self.length + 1 or self.length >= self.size:
            return False
        for i in range(self.length, pos - 1, -1):
            self.data[i] = self.data[i - 1]
        self.data[pos - 1] = value
        self.length += 1
        return True

    def delete(self, pos):
        if pos < 1 or pos > self.length:
            return None
        value = self.data[pos - 1]
        for i in range(pos, self.length):
            self.data[i - 1] = self.data[i]
        self.length -= 1
        return value

    def locate(self, value):
        for i in range(self.length):
            if self.data[i] == value:
                return i + 1
        return 0

​

三、线性表的链式存储

3.1 单链表

单链表是通过指针将线性表的元素链接起来的一种存储结构。每个节点包含两个部分:

  • 数据域:存储数据元素

  • 指针域:存储下一个节点的地址

单链表的特点:

  • 优点:插入和删除操作不需要移动元素,只需修改指针

  • 缺点:不支持随机访问,访问速度较慢

3.2 单链表的基本运算

插入操作

在单链表中插入一个新节点,需要修改前驱节点的指针

初始状态:head -> a -> b -> c -> NULL
插入位置:a和b之间,插入x
操作后:head -> a -> x -> b -> c -> NULL

删除操作

在单链表中删除一个节点,需要修改前驱节点的指针,跳过被删除的节点。

初始状态:head -> a -> b -> c -> NULL
删除位置:b
操作后:head -> a -> c -> NULL

3.3 单链表的代码实现

C语言实现
#include 
#include 

typedef struct Node {
    int data;
    struct Node *next;
} Node, *LinkedList;

// 初始化
void InitList(LinkedList *L) {
    *L = (LinkedList)malloc(sizeof(Node));
    (*L)->next = NULL;
}

// 插入
void Insert(LinkedList L, int pos, int value) {
    LinkedList p = L;
    int i = 0;
    while (p && i < pos - 1) {
        p = p->next;
        i++;
    }
    if (!p || i > pos - 1) return;
    LinkedList newNode = (LinkedList)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = p->next;
    p->next = newNode;
}

// 删除
int Delete(LinkedList L, int pos, int *value) {
    LinkedList p = L;
    int i = 0;
    while (p->next && i < pos - 1) {
        p = p->next;
        i++;
    }
    if (!p->next || i > pos - 1) return 0;
    LinkedList q = p->next;
    *value = q->data;
    p->next = q->next;
    free(q);
    return 1;
}

// 查找
LinkedList Locate(LinkedList L, int value) {
    LinkedList p = L->next;
    while (p) {
        if (p->data == value)
            return p;
        p = p->next;
    }
    return NULL;
}
Python实现
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = Node()

    def insert(self, pos, value):
        p = self.head
        i = 0
        while p and i < pos - 1:
            p = p.next
            i += 1
        if not p or i > pos - 1:
            return
        new_node = Node(value)
        new_node.next = p.next
        p.next = new_node

    def delete(self, pos):
        p = self.head
        i = 0
        while p.next and i < pos - 1:
            p = p.next
            i += 1
        if not p.next or i > pos - 1:
            return None
        q = p.next
        p.next = q.next
        return q.data

    def locate(self, value):
        p = self.head.next
        while p:
            if p.data == value:
                return p
            p = p.next
        return None

3.4 循环链表

循环链表是单链表的一种变体,其尾节点的指针域指向头节点形成一个环。循环链表的特点:

  • 没有明显的结束标志,需要通过遍历判断是否回到起点

  • 适用于需要频繁从尾部插入或删除的场景

循环链表的代码实现(C语言)
#include 
#include 

typedef struct Node {
    int data;
    struct Node *next;
} Node, *CirList;

// 初始化
void InitList(CirList *L) {
    *L = (CirList)malloc(sizeof(Node));
    (*L)->next = *L; // 指向自身形成循环
}

// 插入
void Insert(CirList L, int pos, int value) {
    CirList p = L;
    int i = 0;
    while (p->next != L && i < pos - 1) {
        p = p->next;
        i++;
    }
    if (p->next == L && i < pos - 1) return;
    CirList newNode = (CirList)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = p->next;
    p->next = newNode;
}

// 删除
int Delete(CirList L, int pos, int *value) {
    CirList p = L;
    int i = 0;
    while (p->next != L && i < pos - 1) {
        p = p->next;
        i++;
    }
    if (p->next == L || i > pos - 1) return 0;
    CirList q = p->next;
    *value = q->data;
    p->next = q->next;
    free(q);
    return 1;
}

四、线性表的性能分析与应用场景

4.1 性能对比

操作 顺序存储 单链表 循环链表
插入 O(n) O(n) O(n)
删除 O(n) O(n) O(n)
查找 O(1)(随机访问) O(n) O(n)
空间性能 需预先分配固定大小空间 动态分配,无浪费 动态分配,无浪费
适用场景 频繁查找的场景 频繁插入/删除的场景 需要从尾部频繁操作的场景

4.2 应用场景

  • 顺序存储:适用于数据量较小且查找频繁的场景,例如数组实现的栈和队列。

  • 单链表:适用于数据量较大且插入/删除频繁的场景,例如实现动态内存分配。

  • 循环链表:适用于需要从尾部频繁操作的场景,例如循环队列。

五、总结

    线性表是数据结构的基础,理解它的逻辑结构和存储结构是学习其他复杂数据结构的前提。顺序存储和链式存储各有优缺点,选择合适的存储结构需要根据具体的应用场景权衡。通过本文的讲解和代码示例,相信读者已经对线性表有了全面的理解,可以灵活运用到实际开发中。

你可能感兴趣的:(数据结构,python,c语言)