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

冒泡排序与选择排序以及单链表与双链表

1. 冒泡排序(Bubble Sort)

1. 原理

冒泡排序是一种 简单的排序算法,通过 两两比较相邻元素,把较大的元素逐渐 “冒泡” 到数组末尾。

  • 思路:

    1. 从数组头开始,比较相邻两个元素。

    2. 如果前一个比后一个大,则交换它们。

    3. 一次完整遍历后,最大值会移动到数组末尾。

    4. 对剩下未排序的部分重复以上步骤,直到全部排序完成。

  • 举例:

数组:[5, 3, 8, 4]

  1. 第一次遍历:

    • 5 vs 3 → 交换 → [3,5,8,4]

    • 5 vs 8 → 不交换 → [3,5,8,4]

    • 8 vs 4 → 交换 → [3,5,4,8]

    • 最大值 8 已经到最后

  2. 第二次遍历:

    • 3 vs 5 → 不交换 → [3,5,4,8]

    • 5 vs 4 → 交换 → [3,4,5,8]

    • 第二大值 5 到位

  3. 第三次遍历:

    • 3 vs 4 → 不交换 → [3,4,5,8]

    • 排序完成


2. C语言实现

#include <stdio.h>
void bubbleSort(int arr[], int n);int main(int argc,const char * argv[]) {int arr[] = {5, 3, 8, 4};int n = sizeof(arr) / sizeof(arr[0]);bubbleSort(arr, n);printf("排序结果:");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}void bubbleSort(int arr[], int n) 
{int i, j, temp;for (i = 0; i < n - 1; i++) // 外层控制遍历次数{          for (j = 0; j < n - i - 1; j++) // 内层比较相邻元素{  if (arr[j] > arr[j + 1])  // 前大于后则交换{    temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}

3. 特点

  1. 稳定性:稳定排序(相同元素的顺序不变)

  2. 空间复杂度:O(1),原地排序

  3. 时间复杂度

    • 最好情况(已排序):O(n) 可以优化,设置标志位

    • 最坏/平均情况:O(n²)


4. 优化小技巧

  • 可以加一个 标志位 flag,如果某次遍历没有交换元素,则提前退出(数组已经有序):

for (i = 0; i < n - 1; i++) 
{int swapped = 0;for (j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = 1;}}if (!swapped) // 提前结束{break;} 
}

✅ 总结:冒泡排序思路直观,适合小规模数据,但对大数据效率低(O(n²))。

2. 选择排序(Selection Sort)

1. 原理

选择排序是一种 简单直观的排序算法,每一次从 未排序部分选择最小(或最大)元素 放到已排序部分的末尾(或开头)。

  • 思路:

    1. 将整个数组分为 已排序区未排序区

    2. 每次在未排序区找到最小元素。

    3. 将最小元素与未排序区第一个元素交换。

    4. 重复以上步骤,直到数组全部排序。

  • 举例:

数组:[5, 3, 8, 4]

  1. 第一次选择:

    • 未排序区 [5,3,8,4] → 最小值 3

    • 交换 3 和第一个元素 5 → [3,5,8,4]

  2. 第二次选择:

    • 未排序区 [5,8,4] → 最小值 4

    • 交换 4 和第一个未排序元素 5 → [3,4,8,5]

  3. 第三次选择:

    • 未排序区 [8,5] → 最小值 5

    • 交换 5 和第一个未排序元素 8 → [3,4,5,8]

  4. 排序完成

2. C语言实现

#include <stdio.h>
void selectionSort(int arr[], int n);int main(int argc,const char * argv[]) {int arr[] = {5, 3, 8, 4};int n = sizeof(arr) / sizeof(arr[0]);selectionSort(arr, n);printf("排序结果:");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}void selectionSort(int arr[], int n) 
{int i, j, minIndex, temp;for (i = 0; i < n - 1; i++) // 外层控制已排序区{          minIndex = i; // 假设未排序区第一个是最小值                     for (j = i + 1; j < n; j++) // 遍历未排序区{      if (arr[j] < arr[minIndex]) {minIndex = j;  // 更新最小元素索引              }}     // 交换最小值到已排序区末尾if (minIndex != i) {temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}
}

3. 特点

  1. 稳定性:不稳定(相同元素的顺序可能被交换)

  2. 空间复杂度:O(1),原地排序

  3. 时间复杂度

    • 最好/最坏/平均:O(n²)

    • 每次都必须扫描未排序区,无法提前退出

  4. 交换次数少

    • 每轮只交换一次(选最小值),相比冒泡排序可能减少交换次数


4. 冒泡排序 vs 选择排序对比

特性冒泡排序选择排序
思路相邻元素两两比较、交换每次找到最小值放到已排序区
稳定性稳定不稳定
时间复杂度O(n²),可优化最好情况 O(n)O(n²),最好最坏情况相同
交换次数多,可能每次比较都交换少,每轮只交换一次
适合数据量小规模、部分有序小规模、无序或大数据交换少需求

✅ 总结:

  • 冒泡排序:直观、稳定,但交换次数多

  • 选择排序:交换次数少,但不稳定,比较次数不变

3.单链表(Singly Linked List)

1. 概念

单链表是一种 链式存储结构,由一系列 节点(Node) 组成,每个节点包含:

  1. 数据域(data):存放节点数据

  2. 指针域(next):指向下一个节点

特点:

  • 节点在内存中 不必连续分配,通过指针链接

  • 第一个节点称为 头节点(head)

  • 最后一个节点的 next 指针为 NULL

示意图:

head → [data|next] → [data|next] → [data|next] → NULL


2. C语言节点定义

#include <stdio.h>
#include <stdlib.h>// 定义单链表节点结构体
typedef struct Node 
{int data;           // 数据域struct Node* next;  // 指针域,指向下一个节点
} Node;//错误写法
typedef struct Node 
{int data;           // 数据域struct Node* next;  // 指针域,指向下一个节点
} *Node; //有歧义不明白定义的是一个结构体指针还是结构体

3. 创建单链表(头插法)

头插法:新节点插入到链表头部

Node* createListHead(int arr[], int n) 
{Node* head = NULL;for (int i = 0; i < n; i++) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = arr[i];newNode->next = head; // 插入到头部head = newNode;}return head;
}

4. 链表遍历

void printList(Node* head) 
{Node* p = head;while (p != NULL) {printf("%d -> ", p->data);p = p->next;}printf("NULL\n");
}

5. 链表插入(指定位置)

在第 pos 个位置插入新节点(1表示头节点之后):

void insertNode(Node** head, int pos, int value) 
{Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = value;if (pos == 1) // 头插{ newNode->next = *head;*head = newNode;return;}Node* p = *head;for (int i = 1; i < pos - 1 && p != NULL; i++) {p = p->next;}if (p == NULL)// 位置非法{ return; }newNode->next = p->next;p->next = newNode;
}

6. 链表删除(指定位置)

删除第 pos 个节点:

void deleteNode(Node** head, int pos) 
{if (*head == NULL) return;Node* temp;if (pos == 1) // 删除头节点{ temp = *head;*head = (*head)->next;free(temp);return;}Node* p = *head;for (int i = 1; i < pos - 1 && p->next != NULL; i++) {p = p->next;}if (p->next == NULL) // 位置非法{return; {temp = p->next;p->next = temp->next;free(temp);
}

7. 链表销毁

void freeList(Node* head) 
{Node* temp;while (head != NULL) {temp = head;head = head->next;free(temp);}
}

8. 示例程序

int main(int argc,const char *argv[]) 
{int arr[] = {1, 2, 3};Node* head = createListHead(arr, 3);printf("初始链表: ");printList(head);insertNode(&head, 2, 9); // 在第二个位置插入9printf("插入9后: ");printList(head);deleteNode(&head, 1);    // 删除第一个节点printf("删除第一个节点后: ");printList(head);freeList(head);           // 释放链表return 0;
}

9. 特点

  1. 动态存储:不需要连续内存

  2. 插入/删除方便:时间复杂度 O(1)(若已有指针)

  3. 访问慢:查找元素需从头开始,时间复杂度 O(n)

  4. 节省空间:不需预分配大数组

4.双链表原理

  1. 结构

    • 每个节点包含三个部分:

      1. 数据域(data):存储节点数据

      2. 前驱指针(prev):指向前一个节点

      3. 后继指针(next):指向下一个节点

    示意:

    NULL <- [prev|data|next] <-> [prev|data|next] <-> [prev|data|next] -> NULL

  2. 特点

    • 可以 双向遍历(从头到尾或从尾到头)

    • 插入和删除节点比单链表更方便(可以直接找到前驱节点)

    • 节点多一个指针域,占用更多内存

  3. 操作

    • 插入:更新前驱和后继指针即可

    • 删除:同时修改前驱和后继节点指针

    • 查找:仍需从头或尾遍历

  4. 适用场景

    • 需要 双向遍历

    • 经常在 中间插入/删除

    • 例如浏览器历史记录、LRU缓存


简单总结:

  • 单链表只用 next,只能单向遍历

  • 双链表用 prev + next,支持双向遍历和方便删除

具体示例请看

学生信息管理系统 —— 课程设计报告(C语言有头双向链表)-CSDN博客


文章转载自:

http://Ydu5o6IP.mjbjq.cn
http://8d5crqlY.mjbjq.cn
http://lS4yIyav.mjbjq.cn
http://sA38uK3s.mjbjq.cn
http://7jwM8c8K.mjbjq.cn
http://haJZ53Ba.mjbjq.cn
http://lnyFGGEP.mjbjq.cn
http://maRCHbnD.mjbjq.cn
http://12p1Bosa.mjbjq.cn
http://93KIUPhK.mjbjq.cn
http://Jcsv7VB8.mjbjq.cn
http://iBvnAbHl.mjbjq.cn
http://KgCgsByo.mjbjq.cn
http://qaLRhGzr.mjbjq.cn
http://smChZhXh.mjbjq.cn
http://YUjqv0fE.mjbjq.cn
http://Vpw7SiIJ.mjbjq.cn
http://EZEJLK81.mjbjq.cn
http://5ndVzEs0.mjbjq.cn
http://ochMQC5K.mjbjq.cn
http://tMfriU3A.mjbjq.cn
http://OMZ3Ntl3.mjbjq.cn
http://mVYjjCGT.mjbjq.cn
http://zyGdo3D9.mjbjq.cn
http://C1JLdfcL.mjbjq.cn
http://p0FZRqbs.mjbjq.cn
http://uLANAcVT.mjbjq.cn
http://xZkz3XCV.mjbjq.cn
http://cv6DhliM.mjbjq.cn
http://m5KnQyqX.mjbjq.cn
http://www.dtcms.com/a/381477.html

相关文章:

  • 垂直大模型的“手术刀”时代:从蒙牛MENGNIU.GPT看AI落地的范式革命
  • 【高并发内存池】六、三种缓存的回收内存过程
  • 缓存常见问题与解决方案
  • 【pure-admin】登录页面代码详解
  • 初学鸿蒙笔记-真机调试
  • 反序列化漏洞详解
  • 使用 vue-virtual-scroller 实现高性能传输列表功能总结
  • python 实现 transformer 的 position embeding
  • 003 cargo使用
  • 制作一个简单的vscode插件
  • 【算法详解】:从 模拟 开始打开算法密匙
  • kubeadm搭建生产环境的单master多node的k8s集群
  • RocketMQ存储核心:MappedFile解析
  • 7.k8s四层代理service
  • Stable Virtual Camera:Stability AI等推出的AI模型 ,2D图像轻松转3D视频
  • Golang并发编程及其高级特性
  • 给AI配一台手机+电脑?智谱AutoGLM上线!
  • 怎么在手机上选择一款好用的桌面待办清单工具
  • 傲琪人工合成石墨片:破解智能手机散热困境的创新解决方案
  • LeetCode 刷题【74. 搜索二维矩阵、75. 颜色分类、76. 最小覆盖子串】
  • 【Linux】【实战向】Linux 进程替换避坑指南:从理解 bash 阻塞等待,到亲手实现能执行 ls/cd 的 Shell
  • SRE 系列(七)| 从技术架构到团队组织
  • 网络安全-vulnhub-Web developer 1
  • 国产延时芯片EH3B05上电延时3秒开关机芯片方案超低功耗
  • vivado下载程序后不弹出ila窗口
  • 【VC】 error MSB8041: 此项目需要 MFC 库
  • S7-200 SMART PLC 安全全指南:配置、漏洞解析与复现防护
  • 点可云进销存商城如何部署在微信小程序
  • 安卓学习 之 界面切换
  • 从 IDE 到 CLI:AI 编程代理工具全景与落地指南(附对比矩阵与脚本化示例)