算法学习 | 快速排序(单向扫描分区法、双向扫描分区法、三指针分区法)+三种优化方法(java描述)

分治思想

  • 分治法(divide and conquer,C&D):将问题划分成若干个规模较小而结构与原问题一致的子问题;递归地解决这些问题,然后再合并其结果,就得到原问题的解。
  • 容易确定运行时间,是分治算法的优点之一
  • 分治模式在每一层递归上都有的三个步骤
    分解:将原问题分解成一系列子问题
    解决:递归地解决各子问题,子问题足够小时,则直接有解(可以人工算出)
    合并:将子问题的结果合并成原问题的解
  • 分治的关键点
    ① 原问题可以一直分解为形式相同的子问题,当子问题规模较小时,可以自然求解,如排序:当划分最后只剩下一个元素,而一个元素本身有序
    ② 子问题的解可以通过合并可以得到原问题的解
    ③ 子问题的分解以及解的合并一定是比较简单的,否则分解和合并所花的时间可能超出暴力解法,得不偿失

快速排序–3种实现方式

  • 快速排序运用了分治思想
    分解:将数组arr[ l…r ]划分为两个子数组arr[ l…p-1 ]和arr[ p+1…r ]。使得arr[ p ]为大小居中的数,即左侧arr[ l…p-1 ]中的每个元素都小于等于它;而右侧arr[ p+1…r ]中的每个元素都大于等于它。其中计算下标p也是划分过程的一部分。
    解决:通过递归调用快速排序,对子数组arr[ l…p-1 ]和arr[ p+1…r ]进行排序。
    合并:因为子数组都是原址排序的,所以不需要合并。即A[ l…r ]已经有序。
  • 快排的重点在于划分区域:将小于中间值的元素全放在左边,大于中间值的元素放在右边
  • 本文列出了三种快速排序的方法:
    单向扫描分区法
    双向扫描分区法
    三指针扫描分区法

前期准备:自定义数组工具MyArrays类

  • 自定义了一个数组工具类MyArrays,包含几个数组处理函数,方便在快速排序中调用
    int[] getArr(int length, int min, int max):自动生成一个数组,方便测试时调用
    void swap(int[] arr, int i, int j):交换数组两元素的位置
    void insertSort(int[] arr, int l, int r):插入排序
    int getIndex(int[] arr, int target):获取指定元素的索引
    int getMidValue(int[] arr, int l, int r):获取数组的中值(是元素值位于中间的元素,不是索引位置在中间)
  • 具体代码见下:
/**
 * 自定义数组工具类
 * 
 * @author LRRacac
 *
 */
public class MyArrays {
	/**
	 * 自动生成一个长度为length,元素值介于[min,max]之间的数组
	 * 
	 * @param length---待生成数组的长度
	 * @param min---数组元素的最小值
	 * @param max---数组元素的最大值
	 * @return int[]---返回生成的数组
	 */
	public static int[] getArr(int length, int min, int max) {
		int[] arr = new int[length];
		for (int i = 0; i < arr.length; i++) {
			// 随机生成一个元素值介于[min,max]内的一个元素
			arr[i] = (int) (Math.random() * (max - min + 1) + min);
		}
		return arr;
	}

	/**
	 * 交换数组arr中两元素的位置---即arr[i]与arr[j]位置对调
	 * 
	 * @param arr---待交换元素的数组
	 * @param i---待交换元素的位置
	 * @param j---待交换元素的位置
	 */
	public static void swap(int[] arr, int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}

	/**
	 * 实现插入排序,对数组的索引位置于[l,r]内的部分进行插入排序
	 * 
	 * @param arr---待部分排序的数组
	 * @param l---待排序部分的左边界
	 * @param r---待排序部分的右边界
	 */
	public static void insertSort(int[] arr, int l, int r) {
		for (int i = l + 1; i < r + 1; i++) {
			// 定义当前数组最后一个元素的索引位置
			int lastIndex = i - 1;
			// 定义当前待插入的新值
			int value = arr[i];
			while (lastIndex >= l && arr[lastIndex] > value) {
				// 当带插入的value小于当前数组最后一个元素时,最后一个元素后挪
				arr[lastIndex + 1] = arr[lastIndex];
				// 指针前移,循环判断,所有大于value的元素均后挪
				lastIndex--;
			}
			// 至此,当前arr[lastIndex]<value,所以应当插入在这个元素后边
			arr[lastIndex + 1] = value;

		}
	}

	/**
	 * 获取数组中某元素值的索引,当数组中不含此元素时返回-1
	 * 
	 * @param arr---数组
	 * @param target---目标值
	 * @return int---返回数组目标值的索引
	 */
	public static int getIndex(int[] arr, int target) {
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == target) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 获取数组的中值
	 * 
	 * @param arr---待获取中值的数组
	 * @param l---左边界
	 * @param r---右边界
	 * @return int---返回数组的中值
	 */
	public static int getMidValue(int[] arr, int l, int r) {
		int size = r - l + 1; // 数组长度
		// 每5个元素分为一组,groupSize记录组数
		int groupSize = (size % 5 == 0) ? (size / 5) : (size / 5 + 1);
		// 创建midValues数组存放每组的中值
		int[] midValues = new int[groupSize];
		// 循环获取每组的中值
		for (int i = 0; i < groupSize; i++) {
			if (i == groupSize - 1) {
				insertSort(arr, l + i * 5, r);
				midValues[i] = arr[(l + r) / 2];
			} else {
				insertSort(arr, l + i * 5, l + i * 5 + 4);
				midValues[i] = arr[l + i * 5 + 2];
			}
		}
		// 对存放每组中值的数组进行插入排序
		insertSort(midValues, 0, midValues.length - 1);
		// 返回中值
		return midValues[midValues.length / 2];
	}
}

方式一:单向扫描分区法

  • 分区方法:
    int single_Portion(int[] arr, int l, int r)
    ① 定义数组第一个元素为主元
    ② 定义左指针指向数组第二个元素,右指针指向数组最后一个元素(最终目的是使左指针左侧都是小于等于主元的元素,右指针右侧都是大于主元的元素)
    ③ 判断左指针所指元素是否小于等于主元
    1.若小于等于主元:左指针右移一位
    2.若大于主元:将左指针所指元素与右指针所指元素交换位置,且右指针左移一位
    ④ 直到左指针超过右指针,此时左指针指向大于主元的第一个元素,右指针指向小于等于主元的最后一个元素
    ⑤ 将主元与右指针所指元素交换位置,此时即分区完毕,形成主元左侧元素小于等于主元,主元右侧元素大于主元

  • 快速排序方法(递归实现):
    void single_QuickSort(int[] arr, int l, int r)
    ① 调用分区方法Portion(),获取主元位置
    ② 将主元左侧与右侧分别进行快速排序,即实现了数组的排序

  • 图解:
    单向扫描分区法

  • 具体代码见下:

/**
 * 快速排序-单向扫描分区法
 * 
 * @author LRRacac
 *
 */

public class Single_QuickSort {
	/**
	 * 快速排序函数
	 * 
	 * @param arr---待排序的数组
	 * @param l---待排序数组的左边界
	 * @param r---待排序数组的右边界
	 */
	public static void single_QuickSort(int[] arr, int l, int r) {
		// 当左边界>=右边界时结束程序
		if (l >= r) {
			return;
		}
		// 获取分区后的主元位置
		int pivotIndex = single_Portion(arr, l, r);
		// 对主元左侧的所有元素进行快速排序
		single_QuickSort(arr, l, pivotIndex - 1);
		// 对主元右侧的所有元素进行快速排序
		single_QuickSort(arr, pivotIndex + 1, r);
	}

	/**
	 * 划分区域函数-单向扫描分区法 划分数组中元素的排列,返回主元pivot的位置
	 * 形成:在pivot左侧元素值都小于pivot,在pivot右侧元素值都大于pivot
	 * 
	 * @param arr---待划分的数组
	 * @param l---待划分数组的左边界
	 * @param r---待划分数组的右边界
	 * @return int---返回主元pivot的位置
	 */
	public static int single_Portion(int[] arr, int l, int r) {
		// 初始化主元为l左边界元素
		int pivot = arr[l];
		// 定义左指针-扫描指针,初始指向为[l,r]区间第二个元素
		int scan_left = l + 1;
		// 定义右指针,初始指向为[l,r]区间最后一个元素
		int Scan_right = r;

		/*
		 * 边界判断: 当左指针与右指针相遇时(指向同一个元素,则该元素为最后一个待判断元素) 
		 * 若该元素值<pivot,则左指针右移scan_left++
		 * 若该元素值>pivot,则右指针左移scan_right-- 
		 * 则不论是哪种情况scan_right都小于scan_left 即当scan_left >
		 * scan_right时跳出循环
		 */
		while (scan_left <= Scan_right) {
			if (arr[scan_left] < pivot) {
				// 当左指针所在元素小于pivot时指针右移
				scan_left++;
			} else {
				// 当左指针所在元素大于pivot时,与右指针所指元素交换位置---即将大于pivot的元素放在pivot右边
				MyArrays.swap(arr, scan_left, Scan_right);
				// 右指针左移
				Scan_right--;
			}
		}
		// 当前右指针位于最后一个<=主元pivot的元素上,即主元应该所在的位置
		MyArrays.swap(arr, l, Scan_right); // 此时形成在主元pivot的左侧都是小于pivot的元素;右侧都是大于pivot的元素
		// 返回主元的位置
		return Scan_right;
	}
}

方式二:双向扫描分区法

  • 分区方法概述(大部分与单向扫描法类似,不再赘述):
    int double_Portion(int[] arr, int l, int r)
    ① 与单向扫描分区法大体类似,区别在于:双向扫描分区法的左指针与右指针同时扫描
    ② 左指针往右扫描,直到遇到第一个大于主元的元素停下;右指针往左扫描,知道遇到第一个小于等于主元的元素停下
    ③ 将两指针所指元素交换位置后,两指针继续扫描重复②
  • 快速排序函数:与单向扫描分区法相同
    void double_QuickSort(int[] arr, int l, int r)
  • 图解:
    双向扫描分区法
  • 具体代码见下:
/**
 * 快速排序-双向扫描分区法
 * @author LRRacac
 *
 */

public class Double_QuickSort {
	/**
	 * 快速排序函数
	 * 
	 * @param arr---待排序的数组
	 * @param l---待排序数组的左边界
	 * @param r---待排序数组的右边界
	 */
	public static void double_QuickSort(int[] arr, int l, int r) {
		// 当左边界>=右边界时结束程序
		if (l >= r) {
			return;
		}
		// 获取分区后的主元位置
		int pivotIndex = double_Portion(arr, l, r);
		// 对主元左侧的所有元素进行快速排序
		double_QuickSort(arr, l, pivotIndex - 1);
		// 对主元右侧的所有元素进行快速排序
		double_QuickSort(arr, pivotIndex + 1, r);
	}

	/**
	 * 划分区域函数-双向扫描分区法 
	 * 划分数组中元素的排列,返回主元pivot的位置
	 * 形成:在pivot左侧元素值都小于pivot,在pivot右侧元素值都大于pivot
	 * 
	 * @param arr---待划分的数组
	 * @param l---待划分数组的左边界
	 * @param r---待划分数组的右边界
	 * @return int---返回主元pivot的位置
	 */
	public static int double_Portion(int[] arr, int l, int r) {
		// 初始化主元为l左边界元素
		int pivot = arr[l];
		// 定义左指针-扫描指针1,初始指向为[l,r]区间第二个元素
		int scan_left = l + 1;
		// 定义右指针-扫描指针2,初始指向为[l,r]区间最后一个元素
		int scan_right = r;
		while (scan_left <= scan_right) {
			while (scan_left <= scan_right && arr[scan_left] <= pivot) // 循环退出时,scan_left一定指向着第一个大于主元的元素位置
				scan_left++;
			while (scan_left <= scan_right && arr[scan_right] > pivot)// 循环退出时,scan_right一定指向着最后一个小于等于主元的元素位置
				scan_right--;
			// 当左指针第一次指向>pivot的元素,且右指针第一次指向<=pivot的元素时,交换两指针的元素
			if (scan_left < scan_right)
				MyArrays.swap(arr, scan_left, scan_right);
		}
		// 此时scan_left指向第一个大于主元的元素位置,scan_right指向着最后一个小于等于主元的元素位置
		// 且scan_right指向的位置即是主元pivot应该在的位置
		MyArrays.swap(arr, l, scan_right);
		// 返回主元的位置
		return scan_right;
	}
}

方式三:三指针分区法

  • 分区方法概述:
    int[] tri_Portion(int[] arr, int l, int r)
    ① 如果数组中存在很多与主元等值的元素,那么在对主元左、右侧进行快速排序时,可以不用对那些与主元等值的元素进行排序
    ② 增设一个等值指针,来标记第一个与主元相等的元素,最终将区域划分为三部分:
    1.左侧为所有小于主元的元素
    2.中间为所有等于主元的元素
    3.右侧为所有大于主元的元素
  • 快速排序方法概述:
    void tri_QuickSort(int[] arr, int l, int r)
    ① 将第一个等于主元的元素的左侧进行快排,将最后一个等于主元的元素的右侧进行快排
  • 图解:
    三指针分区法
  • 具体代码见下:
/**
 * 快速排序-三指针分区法
 * @author LRRacac
 *
 */

public class Tri_QuickSort {
	/**
	 * 快速排序函数
	 * 
	 * @param arr---待排序的数组
	 * @param l---待排序数组的左边界
	 * @param r---待排序数组的右边界
	 */
	public static void tri_QuickSort(int[] arr, int l, int r) {
		// 当左边界>=右边界时结束程序
		if (l >= r) {
			return;
		}
		// 获取分区后的主元的左边界和右边界
		int[] arr1 = tri_Portion(arr, l, r);
		// 对主元左侧的所有元素进行快速排序
		tri_QuickSort(arr, l, arr1[0] - 1);
		// 对主元右侧的所有元素进行快速排序
		tri_QuickSort(arr, arr1[1] + 1, r);
	}

	/**
	 * 划分区域函数-三指针分区法 
	 * 划分数组中元素的排列,返回主元pivot的位置
	 * 形成:在pivot左侧元素值都小于pivot,在pivot右侧元素值都大于pivot
	 * 
	 * @param arr---待划分的数组
	 * @param l---待划分数组的左边界
	 * @param r---待划分数组的右边界
	 * @return int[]---返回一个int类型数组,两个元素值分别为主元的左边界以及右边界
	 */
	public static int[] tri_Portion(int[] arr, int l, int r) {
		// 初始化主元为l左边界元素
		int pivot = arr[l];
		// 定义左指针-扫描指针,初始指向为[l,r]区间第二个元素
		int scan_left = l + 1;
		// 定义等指针,初始指向为[l,r]区间第二个元素
		int scan_equal = l + 1;
		// 定义右指针,初始指向为[l,r]区间最后一个元素
		int scan_right = r;
		while (scan_left <= scan_right) {
			while (scan_left <= scan_right && arr[scan_left] < pivot) {
				// 当元素小于主元pivot时,左指针与等指针均右移
				MyArrays.swap(arr, scan_left, scan_equal);
				scan_left++;
				scan_equal++;

			}
			while (scan_left <= scan_right && arr[scan_left] == pivot) {
				// 左指针右移
				scan_left++;
			}
			while (scan_left <= scan_right && arr[scan_left] > pivot) {
				// 当元素大于主元时,交换左指针所指元素与右指针所指元素位置
				if (scan_left < scan_right) {
					MyArrays.swap(arr, scan_left, scan_right);
				}
				// 右指针左移,左指针右移
				scan_right--;
			}
		}
		// 将主元与等指针前一个元素交换位置,并且将等指针-1
		MyArrays.swap(arr, --scan_equal, l);
		// 用数组arr1来存放与主元等值的元素的左边界和右边界
		int[] arr1 = { scan_equal, scan_right };
		// 返回数组arr1
		return arr1;
	}
}

测试代码

import java.util.Arrays;
/**
 * 快速排序测试类
 * @author LRRacac
 * 
 */
public class QuickSortTest {
	public static void main(String[] args) {
		/*
		 * 测试---单向扫描分区法
		 */
		// 获取一个长度为10,元素值介于[1,30]之间的数组
		int[] arr = MyArrays.getArr(10, 1, 30);
		System.out.println("单向——快速排序前:" + Arrays.toString(arr));
		// 对数组进行快速排序
		Single_QuickSort.single_QuickSort(arr, 0, arr.length - 1);
		System.out.println("单向——快速排序后:" + Arrays.toString(arr));

		/*
		 * 测试---双向扫描分区法
		 */
		int[] arr1 = MyArrays.getArr(10, 1, 30);
		System.out.println("双向——快速排序前:" + Arrays.toString(arr1));
		Double_QuickSort.double_QuickSort(arr1, 0, arr1.length - 1);
		System.out.println("双向——快速排序后:" + Arrays.toString(arr1));

		/*
		 * 测试---三指针分区法
		 */
		int[] arr2 = MyArrays.getArr(10, 1, 50);
		System.out.println("三指针——快速排序前:" + Arrays.toString(arr2));
		Tri_QuickSort.tri_QuickSort(arr2, 0, arr2.length - 1);
		System.out.println("三指针——快速排序后:" + Arrays.toString(arr2));
	}
}

  • 结果展示
    测试结果

快速排序–3种优化方式

  • 上述三种快速排序的分区方法(单向扫描分区、双向扫描分区以及三指针分区法)都是直接设定主元为数组第一个元素,如果要让快速排序起到较好的作用,则要尽可能使主元位于数组元素的中间部分。假设最极端的一种情况,数组为{30,1,5,3,8},主元直接为数组最大元素,实际上递归操作的部分只剩下了左侧,快排没有起到O(nlogn)的效果。
  • 因此下面列出了三种优化方式
    ① 方式1:三点中值法- - -针对主元选择的优化
    ② 方式2:绝对中值法- - -针对主元选择的优化
    ③ 方式3:当数组长度较小时,直接采用插入排序- - -针对排序的优化

优化一:三点中值法

  • 三点中值法:即取数组左边界元素、中间位置元素以及右边界元素,进行比较,取三者的中值定为主元。三点中值法是实际开发中使用得比较多的,虽然具有一定的随机性,但是比下文即将提到的绝对中值法要简便,属于O(1)级别,但是绝对中值的优化本身就占用了O(n)
  • 优化代码见下(仅需改动划分区域函数):
/**
	 * 划分区域函数-单向扫描分区法Pro 
	 * 三点中值法优化版本
	 * 
	 */
	public static int single_Portion(int[] arr, int l, int r) {
		int scan_left = l + 1;
		int Scan_right = r;
		
		/*
		 * 三点中值法优化代码(见下)
		 */
		// 定义中值索引,并初始化中值索引为-1
		int midValueIndex = -1;
		// 定义数组中间元素索引
		int midIndex = (l + r) / 2;
		if ((arr[l] >= arr[midIndex] && arr[l] <= arr[r]) || (arr[l] <= arr[midIndex] && arr[l] >= arr[r])) {
			midValueIndex = l;
		} else if ((arr[midIndex] >= arr[l] && arr[midIndex] <= arr[r])
				|| (arr[midIndex] <= arr[l] && arr[midIndex] >= arr[r])) {
			midValueIndex = midIndex;
		} else {
			midValueIndex = r;
		}
		// 至此中值索引已经获取,为不影响后面程序,将中值交换到首位
		MyArrays.swap(arr, l, midValueIndex);
		// 定义主元为第一个元素(此时第一个元素及为三点中的中值)
		int pivot = arr[l];
		/*
		 * 三点中值法优化代码(见上)
		 */

		while (scan_left <= Scan_right) {
			if (arr[scan_left] < pivot) {
				scan_left++;
			} else {
				MyArrays.swap(arr, scan_left, Scan_right);
				Scan_right--;
			}
		}
		MyArrays.swap(arr, l, Scan_right);
		return Scan_right;
	}

优化二:绝对中值法

  • 绝对中值法:取数组中真正的中值作为主元。取数组中值的代码放在了MyArrays工具类中(上文有提到,可以自行查看)。
  • 优化代码见下(仅需改动划分区域函数):
/**
	 * 划分区域函数-单向扫描分区法Pro 
	 * 绝对中值法优化版本
	 * 
	 */
	public static int single_Portion(int[] arr, int l, int r) {
		int scan_left = l + 1;
		int Scan_right = r;
		
		/*
		 * 绝对中值法优化代码(见下)
		 */
		// 获取绝对中值---使用了自定义数组工具类中的方法
		int midValue = MyArrays.getMidValue(arr, l, r);
		// 获取绝对中值的索引---使用了自定义数组工具类中的方法
		int midValueIndex = MyArrays.getIndex(arr, midValue);
		// 为不影响后续代码,将中值交换至首位
		MyArrays.swap(arr, l, midValueIndex);
		int pivot = arr[l];
		/*
		 * 绝对中值法优化代码(见上)
		 */

		while (scan_left <= Scan_right) {
			if (arr[scan_left] < pivot) {
				scan_left++;
			} else {
				MyArrays.swap(arr, scan_left, Scan_right);
				Scan_right--;
			}
		}
		MyArrays.swap(arr, l, Scan_right);
		return Scan_right;
	}

优化三:待排序列较短时,直接用插入排序

  • 大家都知道,快排的复杂度为O(nlogn),是在n趋于一个较大的数时估算而来的,但实际上应该是n*(logn + 1);而插入排序的复杂度为O(n^2),实际上是n(n-1)/2
    当n=16时:快排n*(logn + 1) = 16*(4+1) - - - 插入排序n(n-1)/2 = 1615/2 - - - 快排更快
    当n=8时:快排n
    (logn + 1) = 8*(3+1) - - - 插入排序n(n-1)/2 = 8*7/2 - - - 插入排序更快
    因此,在数组长度较小时(长度小于或等于8时),直接使用插入排序来优化排序
  • 优化代码见下(仅需改动快速排序函数部分):
/**
	 * 快速排序函数Pro 
	 * 优化:待排序列较短时,直接用插入排序
	 */
	public static void single_QuickSort(int[] arr, int l, int r) {
		if (l >= r) {
			return;
		}
		/*
		 * 待排序列较短时,直接用插入排序,优化代码(见下)
		 */
		if (r - l + 1 <= 8) {
			// 当数组长度小于等于8时直接使用插入排序
			MyArrays.insertSort(arr, l, r);
		} else {
			int pivotIndex = single_Portion(arr, l, r);
			single_QuickSort(arr, l, pivotIndex - 1);
			single_QuickSort(arr, pivotIndex + 1, r);
		}
	}

骚话时刻:
终于轮到我卑微的骚话时刻
最近得知,我可能要4月才开学,心里又难受又窃喜(挺贱的hh)
难受是在于家里环境太不适合学习了(有个正值小学“讨嫌”年纪的妹妹)
窃喜是在于有很多自己可以支配的时间(用来准备考研,是再适合不过了)
总是没有十全十美的事情嘛
就像快速排序
再怎么快也会遇到极端情况,也要进行优化增强健壮性
我们何尝不是呢,就算自己不找麻烦,总有麻烦上找你对吧
我们能做的,不是杞人忧天,不是一蹶不振
而是迎难而上,尽可能做得最好,对得起自己就好了,别那么逞英雄啦(一不小心就嘬了一口鸡汤)
在这里插入图片描述

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向冒泡排序和单向冒泡排序都是冒泡排序的变种,它们的主要区别在于排序过程中元素的比较和交换的方向。 单向冒泡排序算法: - 从左到右依次比较相邻元素,将较大的元素往右移动,直到最大的元素被移到最右端。 - 这样一次排序过程中最大的元素会被移到最后一个位置。 - 重复进行上述步骤,每次排序都能确定一个最大的元素的位置,直到所有元素都按照从小到大的顺序排列。 双向冒泡排序算法: - 同样从左到右依次比较相邻元素,将较大的元素往右移动。 - 但是在一次排序过程中,同时也从右到左依次比较相邻元素,将较小的元素往左移动。 - 这样可以确保每次排序同时确定最大和最小的元素位置。 - 重复进行上述步骤,直到所有元素都按照从小到大的顺序排列。 对比分析: - 双向冒泡排序相较于单向冒泡排序,每次排序过程中可以确定最大和最小元素的位置,从而减少了排序的轮数。 - 单向冒泡排序每次只能确定最大元素的位置,需要多次轮换才能将最小元素移动到正确位置。 - 双向冒泡排序在某些情况下可以比单向冒泡排序更快地完成排序。 - 然而,双向冒泡排序的实现相对复杂一些,需要在代码中同时处理从左到右和从右到左的遍历操作。 综上所述,双向冒泡排序在某些情况下可能会比单向冒泡排序更有效率,但实现上相对复杂一些。选择使用哪种算法取决于具体的需求和实际情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值