【算法基础】三指针排序算法 - JAVA

一、基础概念

1.1 什么是三指针排序

三指针排序是一种特殊的分区排序算法,通过使用三个指针同时操作数组,将元素按照特定规则进行分类和排序。这种算法在处理包含有限种类值的数组时表现出色,最经典的应用是荷兰国旗问题(Dutch National Flag Problem)。

1.2 三指针排序的基本思想

三指针排序的核心思想是:

  • 使用三个指针将数组划分为若干个区域
  • 通过元素交换,确保每个区域内的元素都满足特定条件
  • 一次遍历完成所有元素的分类排序

1.3 时间复杂度与空间复杂度

  • 时间复杂度:O(n),仅需一次遍历
  • 空间复杂度:O(1),只使用常数级别的额外空间(几个指针变量)

二、三指针排序的分类

2.1 按值分类的三指针排序

这是最常见的三指针排序应用,将数组中的元素按照特定值分为三类:

  • 小于基准值的元素
  • 等于基准值的元素
  • 大于基准值的元素

2.2 多值三指针排序

处理有三种不同元素的数组,如荷兰国旗问题中的红、白、蓝三色:

  • 第一类元素(如0或红色)
  • 第二类元素(如1或白色)
  • 第三类元素(如2或蓝色)

三、三指针排序实现(荷兰国旗问题)

3.1 数据结构设计

三指针排序主要操作简单数组,不需要特殊的数据结构设计,只需要三个指针变量:

int low = 0;        // 指向第一类元素(0)的右边界
int mid = 0;        // 遍历指针,指向当前处理的元素
int high = n - 1;   // 指向第三类元素(2)的左边界

3.2 排序算法实现

public class ThreePointerSort {/*** 三指针排序算法(荷兰国旗问题)* 将数组中的0、1、2三种元素排序*/public void sortColors(int[] nums) {int low = 0;        // 0的右边界int mid = 0;        // 当前处理元素int high = nums.length - 1;  // 2的左边界while (mid <= high) {if (nums[mid] == 0) {// 当前元素为0,将其交换到左侧区域swap(nums, low, mid);low++;mid++;} else if (nums[mid] == 1) {// 当前元素为1,保持位置不变mid++;} else { // nums[mid] == 2// 当前元素为2,将其交换到右侧区域swap(nums, mid, high);high--;// 注意:这里mid不递增,因为交换后的元素需要重新检查}}}/*** 交换数组中两个元素的位置*/private void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}

3.3 实现细节

三个区域划分

  • [0, low-1]:所有等于0的元素
  • [low, mid-1]:所有等于1的元素
  • [mid, high]:待处理的元素
  • [high+1, n-1]:所有等于2的元素

处理逻辑

  • 当前元素为0:将其交换到左侧区域,lowmid都向右移动
  • 当前元素为1:保持位置不变,只移动mid
  • 当前元素为2:将其交换到右侧区域,high向左移动,mid不动(需要重新检查交换后的元素)

终止条件

  • mid > high时,所有元素都已处理完毕

四、执行示例

4.1 示例数组

以数组 [2, 0, 1, 2, 1, 0] 为例:

4.2 执行过程

  1. 初始状态low=0, mid=0, high=5
    • 数组:[2, 0, 1, 2, 1, 0]
  2. 第一步nums[mid]=2
    • 交换 nums[mid]nums[high][0, 0, 1, 2, 1, 2]
    • high=4, mid=0 (不变)
  3. 第二步nums[mid]=0
    • 交换 nums[low]nums[mid][0, 0, 1, 2, 1, 2] (实际未变)
    • low=1, mid=1
  4. 第三步nums[mid]=0
    • 交换 nums[low]nums[mid][0, 0, 1, 2, 1, 2] (实际未变)
    • low=2, mid=2
  5. 第四步nums[mid]=1
    • mid=3
  6. 第五步nums[mid]=2
    • 交换 nums[mid]nums[high][0, 0, 1, 1, 2, 2]
    • high=3, mid=3
  7. 第六步nums[mid]=1
    • mid=4
  8. 第七步mid=4 > high=3算法终止
    • 最终数组:[0, 0, 1, 1, 2, 2]

五、三指针扩展应用

5.1 快速排序的三路划分

三路快排是三指针思想的另一个重要应用:

  • 将数组划分为小于、等于、大于基准值的三个部分
  • 特别适合处理有大量重复元素的数组
  • 可显著提高快排效率
public void quickSort3Way(int[] arr, int low, int high) {if (low >= high) return;// 选择基准值int pivot = arr[low];int lt = low;      // 小于区域右边界int gt = high;     // 大于区域左边界int i = low + 1;   // 当前处理元素while (i <= gt) {if (arr[i] < pivot) {swap(arr, lt++, i++);} else if (arr[i] > pivot) {swap(arr, i, gt--);} else {i++;}}// 递归排序小于和大于部分quickSort3Way(arr, low, lt - 1);quickSort3Way(arr, gt + 1, high);
}

5.2 处理特定数据集的分组

三指针排序可用于将数组按特定规则分为三组,如:

  • 负数、零和正数
  • 奇数、能被3整除的数和其他数
  • 特定范围内的元素、低于和高于范围的元素

5.3 优化特定范围查找

当需要找出数组中属于特定值范围的所有元素时,可使用三指针方法快速划分:

public int[] findElementsInRange(int[] nums, int low, int high) {// 使用三指针划分数组int[] result = new int[nums.length];int count = partitionByRange(nums, low, high);// 复制中间区域元素并返回// ...
}private int partitionByRange(int[] nums, int lowVal, int highVal) {int left = 0;int curr = 0;int right = nums.length - 1;while (curr <= right) {if (nums[curr] < lowVal) {swap(nums, left++, curr++);} else if (nums[curr] > highVal) {swap(nums, curr, right--);} else {curr++;}}return right - left + 1; // 返回范围内元素数量
}

六、完整示例程序

6.1 处理三种颜色的完整实现

public class ThreeColorSort {public static void main(String[] args) {int[] colors = {2, 0, 1, 1, 0, 2, 1, 2, 0, 0, 1, 2};System.out.println("排序前: " + arrayToString(colors));sortColors(colors);System.out.println("排序后: " + arrayToString(colors));}public static void sortColors(int[] nums) {int low = 0;        // 0的右边界int mid = 0;        // 当前处理元素int high = nums.length - 1;  // 2的左边界while (mid <= high) {if (nums[mid] == 0) {swap(nums, low++, mid++);} else if (nums[mid] == 1) {mid++;} else { // nums[mid] == 2swap(nums, mid, high--);}}}private static void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}private static String arrayToString(int[] arr) {StringBuilder sb = new StringBuilder("[");for (int i = 0; i < arr.length; i++) {sb.append(arr[i]);if (i < arr.length - 1) {sb.append(", ");}}sb.append("]");return sb.toString();}
}

6.2 基于值范围的三路划分

public class ThreeWayPartition {public static void main(String[] args) {int[] arr = {5, 2, 8, 12, 3, 6, 9, 4, 10, 7};int lowVal = 4;int highVal = 8;System.out.println("原数组: " + arrayToString(arr));System.out.println("划分范围: [" + lowVal + ", " + highVal + "]");partitionByRange(arr, lowVal, highVal);System.out.println("划分后: " + arrayToString(arr));}public static void partitionByRange(int[] nums, int lowVal, int highVal) {int left = 0;int curr = 0;int right = nums.length - 1;while (curr <= right) {if (nums[curr] < lowVal) {swap(nums, left++, curr++);} else if (nums[curr] > highVal) {swap(nums, curr, right--);} else {curr++;}}}private static void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}private static String arrayToString(int[] arr) {StringBuilder sb = new StringBuilder("[");for (int i = 0; i < arr.length; i++) {sb.append(arr[i]);if (i < arr.length - 1) {sb.append(", ");}}sb.append("]");return sb.toString();}
}

七、总结

三指针排序算法是一种高效、简洁的算法,特别适合处理有限种类值的排序问题。

核心要点

  • 一次遍历完成排序
  • 原地操作,不需要额外空间
  • 线性时间复杂度 O(n)
  • 特别适合处理三种不同元素的排序问题

优点

  • 简单易实现
  • 高效(一次遍历)
  • 空间利用率高(原地操作)
  • 稳定性可控

缺点

  • 仅适用于处理有限种类元素的排序
  • 需要明确定义元素的优先级或类别

应用场景

  • 荷兰国旗问题(三色排序)
  • 快速排序的三路划分优化
  • 特定元素分组(如正数、零、负数)
  • 数据预处理和清洗
  • 基于特征值的数据分类

三指针排序是分治思想和指针技术的巧妙结合,体现了算法设计中"简单而高效"的理念。掌握这一技术不仅有助于解决特定问题,更能启发我们思考更广泛的算法设计方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《操作系统真象还原》第十二章(2)——进一步完善内核

文章目录 前言可变参数的原理实现系统调用write更新syscall.h更新syscall.c更新syscall-init.c 实现printf编写stdio.h编写stdio.c 第一次测试main.cmakefile结果截图 完善printf修改main.c 结语 前言 上部分链接&#xff1a;《操作系统真象还原》第十二章&#xff08;1&#…

ICML2021 | DeiT | 训练数据高效的图像 Transformer 与基于注意力的蒸馏

Training data-efficient image transformers & distillation through attention 摘要-Abstract引言-Introduction相关工作-Related Work视觉Transformer&#xff1a;概述-Vision transformer: overview通过注意力机制蒸馏-Distillation through attention实验-Experiments…

深度学习:AI 机器人时代

在科技飞速发展的当下&#xff0c;AI 机器人时代正以汹涌之势席卷而来&#xff0c;而深度学习作为其核心驱动力&#xff0c;正重塑着我们生活与工作的方方面面。 从智能工厂的自动化生产&#xff0c;到家庭中贴心服务的智能助手&#xff0c;再到复杂环境下执行特殊任务的专业机…

《告别试错式开发:TDD的精准质量锻造术》

深度解锁TDD&#xff1a;应用开发的创新密钥 在应用开发的复杂版图中&#xff0c;如何雕琢出高质量、高可靠性的应用&#xff0c;始终是开发者们不懈探索的核心命题。测试驱动开发&#xff08;TDD&#xff09;&#xff0c;作为一种颠覆性的开发理念与方法&#xff0c;正逐渐成…

应用层自定义协议序列与反序列化

目录 一、网络版计算器 二、网络版本计算器实现 2.1源代码 2.2测试结果 一、网络版计算器 应用层定义的协议&#xff1a; 应用层进行网络通信能否使用如下的协议进行通信呢&#xff1f; 在操作系统内核中是以这种协议进行通信的&#xff0c;但是在应用层禁止以这种协议进行…

Excel-CLI:终端中的轻量级Excel查看器

在数据驱动的今天&#xff0c;Excel 文件处理成为了我们日常工作中不可或缺的一部分。然而&#xff0c;频繁地在图形界面与命令行界面之间切换&#xff0c;不仅效率低下&#xff0c;而且容易出错。现在&#xff0c;有了 Excel-CLI&#xff0c;一款运行在终端中的轻量级Excel查看…

百度后端开发一面

mutex, rwmutex 在Go语言中&#xff0c;Mutex&#xff08;互斥锁&#xff09;和RWMutex&#xff08;读写锁&#xff09;是用于管理并发访问共享资源的核心工具。以下是它们的常见问题、使用场景及最佳实践总结&#xff1a; 1. Mutex 与 RWMutex 的区别 Mutex: 互斥锁&#xf…

STM32 IIC总线

目录 IIC协议简介 IIC总线系统结构 IIC总线物理层特点 IIC总线协议层 空闲状态 应答信号 数据的有效性 数据传输 STM32的IIC特性及架构 STM32的IIC结构体 0.96寸OLED显示屏 SSD1306框图及引脚定义 4针脚I2C接口模块原理图 字节传输-I2C 执行逻辑框图 命令表…

【unity游戏开发入门到精通——UGUI】整体控制一个UGUI面板的淡入淡出——CanvasGroup画布组组件的使用

注意&#xff1a;考虑到UGUI的内容比较多&#xff0c;我将UGUI的内容分开&#xff0c;并全部整合放在【unity游戏开发——UGUI】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言CanvasGroup画布组组件参数 实战专栏推荐完结 前言 如果我们想要整体控制…

大型语言模型个性化助手实现

大型语言模型个性化助手实现 目录 大型语言模型个性化助手实现PERSONAMEM,以及用户资料和对话模拟管道7种原位用户查询类型关于大语言模型个性化能力评估的研究大型语言模型(LLMs)已经成为用户在各种任务中的个性化助手,从提供写作支持到提供量身定制的建议或咨询。随着时间…

生成式 AI 的未来

在人类文明的长河中,技术革命始终是推动社会跃迁的核心引擎。从蒸汽机解放双手,到电力点亮黑夜,再到互联网编织全球神经网络,每一次技术浪潮都在重塑人类的生产方式与认知边界。而今天,生成式人工智能(Generative AI)正以一种前所未有的姿态登上历史舞台——它不再局限于…

【序列化与反序列化详解】

文章目录 一、序列化与反序列化是什么&#xff1f;1. 为什么需要序列化&#xff1f;2. 反序列化的作用 二、常见的序列化格式三、不同编程语言的序列化与反序列化示例1. Python 的序列化与反序列化JSON 序列化Pickle 序列化&#xff08;仅限 Python&#xff09; 2. Java 的序列…

【单例模式】简介

目录 概念理解使用场景优缺点实现方式 概念理解 单例模式要保证一个类在整个系统运行期间&#xff0c;无论创建多少次该类的对象&#xff0c;始终只会有一个实例存在。就像操作系统中的任务管理器&#xff0c;无论何时何地调用它&#xff0c;都是同一个任务管理器在工作&#…

目标检测YOLO实战应用案例100讲- 无人机平台下露天目标检测与计数

目录 知识储备 基于YOLOv8改进的无人机露天目标检测与计数 一、环境配置与依赖安装 二、核心代码实现(带详细注释) 1. 改进YOLOv8模型定义(添加注意力机制) 2. 无人机视角数据增强(drone_augment.py ) 3. 多目标跟踪与计数(tracking_counter.py ) 4. 完整推理流…

【在Spring Boot中集成Redis】

在Spring Boot中集成Redis 依赖在application.yml中配置Redis服务地址创建Redis配置类缓存工具类使用 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency&…

计算机视觉——基于树莓派的YOLO11模型优化与实时目标检测、跟踪及计数的实践

概述 设想一下&#xff0c;你在多地拥有多个仓库&#xff0c;要同时监控每个仓库的实时状况&#xff0c;这对于时间和精力而言&#xff0c;都构成了一项艰巨挑战。从成本和可靠性的层面考量&#xff0c;大规模部署计算设备也并非可行之策。一方面&#xff0c;大量计算设备的购…

通信协议记录仪-产品规格书

以下是为 ​​通信协议记录仪(ProtoLogger Pro)​​ 的​​详细产品规格书​​,覆盖 ​​技术细节、场景需求、竞品差异化​​,确保可作为产品开发、市场营销及竞品分析的核心依据。 ​​通信协议记录仪产品规格书​​ ​​产品名称​​:ProtoLogger Pro(中文名称:蹲守…

python:sklearn 决策树(Decision Tree)

5. 决策树&#xff08;Decision Tree&#xff09; - 第5章 算法思想&#xff1a;基于信息增益&#xff08;ID3&#xff09;或基尼不纯度&#xff08;CART&#xff09;递归划分特征。 编写 test_dtree_1.py 如下 # -*- coding: utf-8 -*- """ 5. 决策树&…

【2-sat】2-sat算法内容及真题

A.2-sat简介 2-sat算法可以求解给定推出关系下的一种合法情况。题目中重常常&#xff0c;给定一些布尔变量A、B、C、D…&#xff0c;再给出一系列形如 B ⟶ A , C ⟶ D B \longrightarrow A , C \longrightarrow \neg D B⟶A,C⟶D的推出关系&#xff0c;询问使得所有推出关系…

【git】获取特定分支和所有分支

1 特定分支 1.1 克隆指定分支&#xff08;默认只下载该分支&#xff09; git clone -b <分支名> --single-branch <仓库URL> 示例&#xff08;克隆 某一个 分支&#xff09;&#xff1a; git clone -b xxxxxx --single-branch xxxxxxx -b &#xff1a;指定分支…