当前位置: 首页 > news >正文

数据结构:七大线性数据结构从结构体定义到函数实现的的区别

数据结构概述

在程序设计中,数据结构的选择直接影响算法的效率和代码的可维护性。本文将深入解析顺序表、单链表、双向链表、栈、链栈、队列、循环队列这七种线性数据结构,通过对比它们的结构体定义和函数实现,帮助大家理解各自的特点和适用场景。

1. 顺序表(Sequence List)

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef struct {int data[MAXSIZE];  // 静态数组存储元素int length;         // 当前长度
} SeqList;

结构特点

使用连续内存空间                通过数组下标直接访问元素                需要预先分配固定大小

核心函数实现对比

// 初始化顺序表
void initSeqList(SeqList* list) {list->length = 0;  // 只需设置长度为0
}
// 插入元素
int insertSeqList(SeqList* list, int pos, int elem) {if (pos < 1 || pos > list->length + 1) return 0;if (list->length >= MAXSIZE) return 0;for (int i = list->length; i >= pos; i--) {list->data[i] = list->data[i - 1];  // 移动后续元素}list->data[pos - 1] = elem;list->length++;return 1;
}
// 删除元素
int deleteSeqList(SeqList* list, int pos) {if (pos < 1 || pos > list->length) return 0;for (int i = pos; i < list->length; i++) {list->data[i - 1] = list->data[i];  // 前移覆盖}list->length--;return 1;
}

函数说明表

函数名头文件参数返回值参数示例示例含义
initSeqListstdio.h, stdlib.hlist: 顺序表指针&list初始化空顺序表
insertSeqListstdio.h, stdlib.hlist: 顺序表指针
pos: 位置
elem: 元素
成功1,失败0&list, 2, 10在位置2插入10
deleteSeqListstdio.h, stdlib.hlist: 顺序表指针
pos: 位置
成功1,失败0&list, 3删除位置3元素

2. 单链表(Singly Linked List)

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
typedef struct Node {int data;           // 数据域struct Node* next;  // 指针域
} ListNode;

结构特点

节点包含数据和指针                内存非连续,通过指针链接                动态内存分配

核心函数实现对比

// 创建新节点
ListNode* createNode(int elem) {ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));newNode->data = elem;newNode->next = NULL;  // 新节点next初始为NULLreturn newNode;
}// 插入节点
void insertListNode(ListNode** head, int pos, int elem) {ListNode* newNode = createNode(elem);if (pos == 1) {  // 头插法newNode->next = *head;*head = newNode;return;}ListNode* current = *head;for (int i = 1; i < pos - 1 && current != NULL; i++) {current = current->next;  // 遍历找到插入位置}if (current == NULL) return;newNode->next = current->next;  // 新节点指向原后继current->next = newNode;        // 原前驱指向新节点
}// 删除节点
void deleteListNode(ListNode** head, int pos) {if (*head == NULL) return;ListNode* temp = *head;if (pos == 1) {  // 删除头节点*head = temp->next;free(temp);return;}for (int i = 1; temp != NULL && i < pos - 1; i++) {temp = temp->next;  // 找到删除位置的前驱}if (temp == NULL || temp->next == NULL) return;ListNode* toDelete = temp->next;    // 要删除的节点temp->next = toDelete->next;        // 前驱指向后继free(toDelete);                     // 释放内存
}

函数说明表

函数名头文件参数返回值参数示例示例含义
createNodestdio.h, stdlib.helem: 节点数据新节点指针5创建数据为5的节点
insertListNodestdio.h, stdlib.hhead: 头指针地址
pos: 位置
elem: 元素
&head, 1, 8在头部插入8
deleteListNodestdio.h, stdlib.hhead: 头指针地址
pos: 位置
&head, 2删除第2个节点

3. 双向链表(Doubly Linked List)

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
typedef struct DNode {int data;               // 数据域struct DNode* prev;     // 前驱指针struct DNode* next;     // 后继指针
} DListNode;

结构特点

每个节点有两个指针                可以双向遍历                插入删除操作更复杂但更灵活

核心函数实现对比

// 创建双向节点
DListNode* createDNode(int elem) {DListNode* newNode = (DListNode*)malloc(sizeof(DListNode));newNode->data = elem;newNode->prev = NULL;  // 两个指针都初始化为NULLnewNode->next = NULL;return newNode;
}// 插入节点(双向链表特有)
void insertDListNode(DListNode** head, int pos, int elem) {DListNode* newNode = createDNode(elem);if (*head == NULL) {  // 空链表*head = newNode;return;}if (pos == 1) {  // 头插newNode->next = *head;(*head)->prev = newNode;  // 设置原头节点的前驱*head = newNode;return;}DListNode* current = *head;for (int i = 1; i < pos - 1 && current != NULL; i++) {current = current->next;}if (current == NULL) return;newNode->next = current->next;newNode->prev = current;           // 设置新节点前驱if (current->next != NULL) {current->next->prev = newNode; // 设置后继节点的前驱}current->next = newNode;           // 设置前驱节点的后继
}// 删除节点(双向链表特有)
void deleteDListNode(DListNode** head, int pos) {if (*head == NULL) return;DListNode* current = *head;if (pos == 1) {  // 删除头节点*head = current->next;if (*head != NULL) {(*head)->prev = NULL;  // 新头节点前驱置空}free(current);return;}for (int i = 1; current != NULL && i < pos; i++) {current = current->next;  // 直接找到要删除的节点}if (current == NULL) return;if (current->next != NULL) {current->next->prev = current->prev;  // 更新后继的前驱}if (current->prev != NULL) {current->prev->next = current->next;  // 更新前驱的后继}free(current);
}

4. 栈(Stack)- 顺序实现

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE 100
typedef struct {int data[STACK_SIZE];  // 数组存储int top;               // 栈顶指针
} SeqStack;

结构特点

LIFO(后进先出)                只能在栈顶操作                顺序存储实现

核心函数实现对比

// 初始化栈
void initStack(SeqStack* stack) {stack->top = -1;  // 栈空标志
}// 入栈
int pushStack(SeqStack* stack, int elem) {if (stack->top >= STACK_SIZE - 1) return 0;stack->data[++stack->top] = elem;  // 栈顶上移再赋值return 1;
}// 出栈
int popStack(SeqStack* stack) {if (stack->top < 0) return -1;return stack->data[stack->top--];  // 返回栈顶元素再下移
}// 获取栈顶(顺序栈特有)
int peekStack(SeqStack* stack) {if (stack->top < 0) return -1;return stack->data[stack->top];  // 只读不修改top
}

函数说明表

函数名头文件参数返回值参数示例示例含义
initStackstdio.h, stdlib.hstack: 栈指针&stack初始化空栈
pushStackstdio.h, stdlib.hstack: 栈指针
elem: 元素
成功1,失败0&stack, 55入栈
popStackstdio.h, stdlib.hstack: 栈指针栈顶元素&stack弹出栈顶
peekStackstdio.h, stdlib.hstack: 栈指针栈顶元素&stack查看栈顶

5. 链栈(Linked Stack)

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
typedef struct StackNode {int data;                   // 数据域struct StackNode* next;     // 指针域
} LinkStack;

结构特点

栈顶是链表头                动态内存管理                理论上容量无限

核心函数实现对比

// 入栈(链栈特有)
void pushLinkStack(LinkStack** top, int elem) {LinkStack* newNode = (LinkStack*)malloc(sizeof(LinkStack));newNode->data = elem;newNode->next = *top;  // 新节点指向原栈顶*top = newNode;        // 更新栈顶指针
}// 出栈(链栈特有)
int popLinkStack(LinkStack** top) {if (*top == NULL) return -1;LinkStack* temp = *top;int elem = temp->data;*top = (*top)->next;  // 栈顶下移free(temp);           // 释放原栈顶return elem;
}// 判断空栈
int isEmptyLinkStack(LinkStack* top) {return top == NULL;  // 栈空条件
}

6. 队列(Queue)- 顺序实现

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
#define QUEUE_SIZE 100
typedef struct {int data[QUEUE_SIZE];  // 数组存储int front;             // 队头指针int rear;              // 队尾指针
} SeqQueue;

结构特点

FIFO(先进先出)                队尾插入,队头删除                存在"假溢出"问题

核心函数实现对比

// 初始化队列
void initQueue(SeqQueue* queue) {queue->front = 0;queue->rear = 0;  // 队空条件:front == rear
}// 入队
int enQueue(SeqQueue* queue, int elem) {if (queue->rear >= QUEUE_SIZE) return 0;queue->data[queue->rear++] = elem;  // 队尾插入,rear后移return 1;
}// 出队
int deQueue(SeqQueue* queue) {if (queue->front == queue->rear) return -1;return queue->data[queue->front++];  // 队头取出,front后移
}// 判断队空
int isEmptyQueue(SeqQueue* queue) {return queue->front == queue->rear;  // 队空条件
}

7. 循环队列(Circular Queue)

结构体定义与特点

#include <stdio.h>
#include <stdlib.h>
#define CQ_SIZE 5  // 实际可用CQ_SIZE-1
typedef struct {int data[CQ_SIZE];  // 循环数组int front;          // 队头指针int rear;           // 队尾指针
} CircularQueue;

结构特点

数组首尾相接                解决"假溢出"问题                队空和队满的判断条件特殊

核心函数实现对比

// 初始化循环队列
void initCQueue(CircularQueue* queue) {queue->front = 0;queue->rear = 0;  // 循环队列空条件
}// 入队(循环队列特有)
int enCQueue(CircularQueue* queue, int elem) {if ((queue->rear + 1) % CQ_SIZE == queue->front) return 0;  // 队满queue->data[queue->rear] = elem;queue->rear = (queue->rear + 1) % CQ_SIZE;  // 循环后移return 1;
}// 出队(循环队列特有)
int deCQueue(CircularQueue* queue) {if (queue->front == queue->rear) return -1;  // 队空int elem = queue->data[queue->front];queue->front = (queue->front + 1) % CQ_SIZE;  // 循环后移return elem;
}// 判断队满(循环队列特有)
int isCQueueFull(CircularQueue* queue) {return (queue->rear + 1) % CQ_SIZE == queue->front;  // 队满条件
}

函数说明表

函数名头文件参数返回值参数示例示例含义
initCQueuestdio.h, stdlib.hqueue: 队列指针&queue初始化循环队列
enCQueuestdio.h, stdlib.hqueue: 队列指针
elem: 元素
成功1,失败0&queue, 3元素3入队
deCQueuestdio.h, stdlib.hqueue: 队列指针队头元素&queue队头出队
isCQueueFullstdio.h, stdlib.hqueue: 队列指针队满1,否0&queue判断队满

实际应用示例

浏览器历史记录(栈的应用)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_HISTORY 10typedef struct {char* pages[MAX_HISTORY];int top;
} BrowserHistory;void initBrowser(BrowserHistory* browser) {browser->top = -1;
}void visitPage(BrowserHistory* browser, const char* url) {if (browser->top >= MAX_HISTORY - 1) {free(browser->pages[0]);for (int i = 0; i < MAX_HISTORY - 1; i++) {browser->pages[i] = browser->pages[i + 1];}browser->top--;}browser->pages[++browser->top] = strdup(url);
}char* goBack(BrowserHistory* browser) {if (browser->top <= 0) return NULL;return browser->pages[--browser->top];
}char* goForward(BrowserHistory* browser) {if (browser->top >= MAX_HISTORY - 1) return NULL;return browser->pages[++browser->top];
}

数据结构对比总结

内存分配方式

数据结构存储方式内存连续性容量限制
顺序表静态数组连续固定大小
单链表动态节点不连续理论无限
双向链表动态节点不连续理论无限
顺序栈静态数组连续固定大小
链栈动态节点不连续理论无限
顺序队列静态数组连续固定大小
循环队列静态数组连续固定大小

操作复杂度对比

操作顺序表单链表双向链表队列
访问O(1)O(n)O(n)O(1)O(1)
插入O(n)O(1)O(1)O(1)O(1)
删除O(n)O(1)O(1)O(1)O(1)
查找O(n)O(n)O(n)O(n)O(n)

常见面试题

1. 基础概念题

Q1:顺序表和链表的区别是什么?

存储方式:顺序表连续,链表离散                访问效率:顺序表O(1),链表O(n)

插入删除:顺序表O(n),链表O(1)                空间效率:顺序表更优,链表有指针开销

Q2:栈和队列的主要区别?

栈:LIFO(后进先出),只能在栈顶操作

队列:FIFO(先进先出),队尾插入,队头删除

2. 算法实现题

Q3:用两个栈实现队列

typedef struct {SeqStack stack1;  // 用于入队SeqStack stack2;  // 用于出队
} StackQueue;void enStackQueue(StackQueue* sq, int elem) {pushStack(&sq->stack1, elem);
}int deStackQueue(StackQueue* sq) {if (isEmptyStack(&sq->stack2)) {while (!isEmptyStack(&sq->stack1)) {pushStack(&sq->stack2, popStack(&sq->stack1));}}return popStack(&sq->stack2);
}

Q4:判断链表是否有环

int hasCycle(ListNode* head) {if (head == NULL) return 0;ListNode* slow = head;ListNode* fast = head;while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;if (slow == fast) return 1;}return 0;
}

3. 综合应用题

Q5:设计LRU缓存机制

使用哈希表+双向链表实现                哈希表提供O(1)访问                双向链表维护访问顺序

Q6:用队列实现栈

typedef struct {SeqQueue queue1;SeqQueue queue2;
} QueueStack;void pushQueueStack(QueueStack* qs, int elem) {enQueue(&qs->queue1, elem);
}int popQueueStack(QueueStack* qs) {while (qs->queue1.front != qs->queue1.rear - 1) {enQueue(&qs->queue2, deQueue(&qs->queue1));}int elem = deQueue(&qs->queue1);SeqQueue temp = qs->queue1;qs->queue1 = qs->queue2;qs->queue2 = temp;return elem;
}

性能优化建议

  1. 顺序结构:适合查询多、增删少的场景

  2. 链式结构:适合频繁插入删除的场景

  3. 栈的应用:函数调用、表达式求值、括号匹配

  4. 队列的应用:消息队列、任务调度、广度优先搜索

总结

通过对比七种线性数据结构的结构体定义和函数实现,我们可以清楚地看到:

  1. 顺序表适合随机访问,但插入删除效率低

  2. 链表适合频繁插入删除,但访问效率低

  3. 适合LIFO场景,操作受限但效率高

  4. 队列适合FIFO场景,解决顺序处理问题

  5. 循环队列解决了顺序队列的假溢出问题

理解这些数据结构的本质区别和适用场景,对于设计高效算法和解决实际问题具有重要意义。在实际开发中,大家根据具体需求选择合适的数据结构,平衡时间效率和空间效率。

http://www.dtcms.com/a/451063.html

相关文章:

  • 开发公司五证包括什么网站运营优化
  • 网站空间购买 北京域名及网站建设实验报告
  • 如何购买建设网站系统微商自己做网站
  • 建设网站需要几个步骤wordpress缩 图
  • 台州黄岩网站建设郑州网站建设 云极
  • 移动端网站开发项目国学底蕴的公司名字
  • 中英语网站制作方法搜资源
  • 东莞网站优化教程网站建设费是广告费吗
  • 苹果发布 RL4HS 框架精准定位 LLM 幻觉
  • 1688网站怎么做网站改版seo
  • 做的比较好的美食网站有哪些网络彩票代理怎么做社区网站
  • 元宇宙的历史教训:从虚拟世界泡沫中学习
  • 网站实名制注册怎么做wordpress短视频模板
  • 网站开发采用了哪些技术苏州网站设计价格
  • 商务网站建设过程企企业业网网站站建建设设
  • 24H2动态壁纸无法正常嵌入(针对vb.net的紧急加更)
  • 中山手机网站建设电话北京seo公司华网白帽
  • 光通信|级联相变超表面实现OAM模式切换
  • wordpress零基础建站云适配 网站
  • 吴桥网站霸气业务网站源码
  • <从零基础到精通JavaScript>1.3 核心原始数据类型
  • C语言 ——— 自定义类型
  • 做网站frontpage 2003织梦网站0day漏洞
  • 龙岗建设网站wordpress录音功能
  • 内蒙网络_网站建设页面关键词优化
  • 企业做网页还是网站公司网站搜索不到
  • 做洁具最好的网站株洲网站建设公司排名
  • Java 队列详解:从基础到实战应用
  • 新网站怎么发外链关键词排名推广方法
  • 宁波网站排名提升网站修改域名