首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >数据结构专题 - 线性表

数据结构专题 - 线性表

作者头像
啊阿狸不会拉杆
发布2026-01-21 10:45:06
发布2026-01-21 10:45:06
1270
举报

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

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

1.1 线性表的逻辑结构

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

  • 每个元素在线性表中都有一个唯一的前驱(除了第一个元素)
  • 每个元素在线性表中都有一个唯一的后继(除了最后一个元素)
  • 元素之间是线性关系(即一对一的关系)

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

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

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

  • 初始化:创建一个空的线性表
  • 插入:在指定位置插入一个新元素
  • 删除:删除指定位置的元素
  • 查找:查找某个元素的位置
  • 遍历:依次访问线性表中的每个元素
  • 清空:清空线性表中的所有元素

1.3 类型定义

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

二、线性表的顺序存储

2.1 顺序存储结构

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

  • 优点:支持随机访问,访问速度快
  • 缺点:插入和删除操作需要移动大量元素

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

插入操作

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

代码语言:javascript
复制
初始状态:[a0, a1, a2, a3, a4]
插入位置:2,插入元素:x
操作后:[a0, a1, x, a2, a3, a4]
删除操作

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

代码语言:javascript
复制
初始状态:[a0, a1, a2, a3, a4]
删除位置:2
操作后:[a0, a1, a3, a4]

2.3 顺序存储的代码实现

C语言实现
代码语言:javascript
复制
​
#include <stdio.h>
#include <stdlib.h>

#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实现
代码语言:javascript
复制
​
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 单链表的基本运算

插入操作

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

代码语言:javascript
复制
初始状态:head -> a -> b -> c -> NULL
插入位置:a和b之间,插入x
操作后:head -> a -> x -> b -> c -> NULL
删除操作

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

代码语言:javascript
复制
初始状态:head -> a -> b -> c -> NULL
删除位置:b
操作后:head -> a -> c -> NULL
3.3 单链表的代码实现
C语言实现
代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

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实现
代码语言:javascript
复制
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语言)
代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

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 应用场景

  • 顺序存储:适用于数据量较小且查找频繁的场景,例如数组实现的栈和队列。
  • 单链表:适用于数据量较大且插入/删除频繁的场景,例如实现动态内存分配。
  • 循环链表:适用于需要从尾部频繁操作的场景,例如循环队列。

五、总结

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、线性表的概念与抽象数据类型
    • 1.1 线性表的逻辑结构
    • 1.2 线性表的抽象数据类型(ADT)
    • 1.3 类型定义
  • 二、线性表的顺序存储
    • 2.1 顺序存储结构
    • 2.2 顺序存储结构上的基本运算
    • 2.3 顺序存储的代码实现
  • 三、线性表的链式存储
    • 3.1 单链表
    • 3.2 单链表的基本运算
      • 插入操作
      • 删除操作
      • 3.3 单链表的代码实现
    • 3.4 循环链表
  • 四、线性表的性能分析与应用场景
    • 4.1 性能对比
    • 4.2 应用场景
  • 五、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档