前言:数组元素排序
排序主要有:
1.选择排序(直接选择排序,堆排序);
2. 交换排序(冒泡排序,快速排序);
3. 插入排序(直接插入排序,二分插入排序,Shell排序);
4. 归并排序;
这里主要写了冒泡排序,选择排序,插入排序;但是在开发中因为性能的问题,我们都不会自己写排序算法,不过排序在笔试中经常见到。
1. 冒泡排序
基本思路:对要未排序的每一个元素从头到尾依次比较相邻的两个元素的大小,若大于则交换位置,经过第一轮的比较得出最大值,然后使用相同的方法把剩下的元素逐个比较即可。
可以看出,若是有N个元素,那么一共要进行N-1轮比较,第M轮要进行 N-M 次比较。(如有6个元素,要进行5轮比较,第一轮比较5次,第二轮比较4次,第三轮比较3次…);
1.1 代码实现
// 冒泡排序算法
public class BubbleSort{
public static void main(String args[]){
int[] nums = {6,3,2,5,1};
bubbleSort(nums); // 开始排序
//printArray(nums); // 打印排序后的数组
}
public static void bubbleSort(int[] nums){
for(int i = 0 ; i < nums.length - 1; i++){ // 要比较的轮数
for(int j = 0; j < nums.length - 1 - i; j++){ //每一轮要比较多次数
int temp; //定义临时变量
temp = nums[j];
if (nums[j] > nums[j+1]){
nums[j] = nums[j+1];
nums[j+1] = temp;
}
System.out.print("第"+(i+1)+"轮"+"第"+(j+1)+"次排序结果:");
printArray(nums);
}
}
}
// 按照格式打印数组
public static void printArray(int[] nums){
String res = "[";
for (int i = 0; i < nums.length; i++){
res = res + nums[i];
if ( i != nums.length - 1){
res = res + ",";
}
}
res = res + "]";
System.out.println(res);
}
}
---------- 运行java ----------
第1轮第1次排序结果:[3,6,2,5,1]
第1轮第2次排序结果:[3,2,6,5,1]
第1轮第3次排序结果:[3,2,5,6,1]
第1轮第4次排序结果:[3,2,5,1,6]
第2轮第1次排序结果:[2,3,5,1,6]
第2轮第2次排序结果:[2,3,5,1,6]
第2轮第3次排序结果:[2,3,1,5,6]
第3轮第1次排序结果:[2,3,1,5,6]
第3轮第2次排序结果:[2,1,3,5,6]
第4轮第1次排序结果:[1,2,3,5,6]
输出完成 (耗时 0 秒) - 正常终止
1.2冒泡排序的优化
冒泡排序技结束的条件:在一轮的比较过程中,没有发生位置上的交换;
因此,我们可以设置一个变量flag,用来标记本轮是否发生了交换。
如果没有发生交换,则flag=0,如果发生交换,则flag=1.
/**
* 优化后的排序算法:
* 冒泡排序技结束的条件:在一轮的比较过程中,没有发生位置上的交换
*
* @param nums
*/
static void bubbleSort(int[] nums) {
// 外循环控制比较的轮数
for (int i = 0; i < nums.length - 1; i++) {
int flag = 0; // 设置标志位
// 内循环控制每一轮比较的次数
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = 1; // 在每一次的比较重,若交换了位置则改变标志位
}
}
if (flag == 0) {
return;
}
}
}
2. 选择排序
基本思路: 选择某个索引位置的元素,然后和后面的元素一次比较,若大于则交换位置,经过第一轮比较排序得出最小值,然后使用相同的方法把剩下的元素逐个比较即可。可以看出:第一轮会选出最小值,第二轮中会选出第二个最小值,直到最后。
第一轮用arry[0]和后面的元素依次比较,第二轮使用arry[1]和后面的元素相比较,一次类推。N个数进行N-1轮比较。
Java选择排序的思想(以从小到大排序为例,从大到小排序于此相同):
在arr[0]到arr[n-1]中选出最小(大)的的数与arr[0]交换位置,
在arr[1]到arr[n-1]中选出最小(大)的的数与arr[1]交换位置,
在arr[2]到arr[n-1]中选出最小(大)的的数与arr[2]交换位置,
在arr[n-2]到arr[n-1]中选出最小(大)的的数与arr[n-2]交换位置,
…
选择排序每一轮只进行一次交换,相对于冒泡排序效率高一些。
2.1代码实现
class SelectSortDemo
{
public static void main(String[] args)
{
int[] nums = {6,3,2,5,1};
selectSort(nums);
printArray(nums);
}
public static void selectSort(int[] nums){
for(int i=0; i < nums.length-1; i++){ //要比较的轮数
for (int j=0; j < nums.length-i-1; j++ ) //每一轮要交换的次数
{
if (nums[i] > nums[i+j+1] )
{
int temp = nums[i]; // 先把当前索引记录在临时变量中
nums[i] = nums[i+j+1];
nums[i+j+1] = temp;
}
System.out.print("第"+(i+1)+"轮"+"第"+(j+1)+"次排序结果:");
printArray(nums);
}
}
}
// 按照格式打印数组
public static void printArray(int[] nums){
String res = "[";
for (int i = 0; i < nums.length; i++){
res = res + nums[i];
if ( i != nums.length - 1){
res = res + ",";
}
}
res = res + "]";
System.out.println(res);
}
}
---------- 运行java ----------
第1轮第1次排序结果:[3,6,2,5,1]
第1轮第2次排序结果:[2,6,3,5,1]
第1轮第3次排序结果:[2,6,3,5,1]
第1轮第4次排序结果:[1,6,3,5,2]
第2轮第1次排序结果:[1,3,6,5,2]
第2轮第2次排序结果:[1,3,6,5,2]
第2轮第3次排序结果:[1,2,6,5,3]
第3轮第1次排序结果:[1,2,5,6,3]
第3轮第2次排序结果:[1,2,3,6,5]
第4轮第1次排序结果:[1,2,3,5,6]
输出完成 (耗时 0 秒) - 正常终止
选择排序到这里并没有结束!!上述的选择排序的代码其实是有点小瑕疵的。在N轮的排序中,第M轮要进行 N-M 次比较。比如,5个元素的排序,第一轮中需要进行4次位置交换,第二轮进行3次位置交换,效率不够高效!其实,在一轮的排序中,完全可以只进行一次位置的交换!
2.2选择排序的优化
public static void selectSort(int[] nums){
for(int i = 0 ; i < nums.length - 1; i++){ // 要比较的轮数
int minValueIndex = i; // 假设一个最小值的索引
for(int j = 0; j < nums.length - 1 - i; j++){ //每一轮要比较多次数
if (nums[minValueIndex] > nums[i+j+1]){
minValueIndex = i+j+1; // 获取到最小值的索引
}
}
if(minValueIndex != i){
int temp = nums[i]; //定义临时变量
nums[i] = nums[minValueIndex];
nums[minValueIndex] = temp;
System.out.print("第"+(i+1)+"轮排序结果:");
printArray(nums);
}
}
}
---------- 运行java ----------
第1轮排序结果:[1,3,2,5,6]
第2轮排序结果:[1,2,3,5,6]
第3轮排序结果:[1,2,3,5,6]
第4轮排序结果:[1,2,3,5,6]
输出完成 (耗时 0 秒) - 正常终止
3. 快速排序
快速排序也是“交换类”的排序,它通过多次划分操作来实现排序。以升序为例,执行过程可以概括为:每一轮比较当前子序列中的一个关键字(通常是第一个)作为枢纽,将子序列中比枢纽小的移动到枢纽前边,比枢纽大的移动到枢纽后面;当本轮所有子序列都被枢纽以上规则划分完毕后得到新的一组更短的子序列,它们将成为下一趟划分的初始序列集。
3.1 代码实现
static void quickSort(int[] nums, int low, int high) {
if (nums == null || nums.length == 1 || low > high) {
return;
}
int i = low, j = high;
int base = nums[low]; // 基准元素
while (i < j) {
// 从后往前,寻找比基准小的元素的索引位置
while (nums[j] >= base && i < j) {
j--;
}
// 从前完后,寻找比基准大的元素的索引位置
while (nums[i] <= base && i < j) {
i++;
}
if (i < j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
nums[low] = nums[i];
nums[i] = base;
// 递归,继续完成左子列的排序
quickSort(nums, low, i - 1);
// 递归,完成右子列的排序
quickSort(nums, i + 1, high);
}