一、选择排序
算法描述
选择排序是内部排序法的其中一种,他是从一组乱序的数据中(n个数),(此处默认从大到小进行排序)每次找一个最小的数,然后将它和第一个元素交换。接下来在剩下的数中,再找到最小数,将它和第二个元素进行交换…直至仅剩一个数为止。所以选择排序所进行的排序次数是n-1次。
算法的思路分析
- 先进行一轮排序:(一轮排序一共有三步)先从list[0]~list[n-1]中找到最小的值;再将它与list[0]进行交换;遍历完整个数组之后就得到了本轮的最小数和最小数的下标。
- 再仿照第一轮,进行第二轮的循环;
- 再进行第三轮的循环;
- 找出这三轮循环的规律,再进行整个数组的排序。
算法的源代码描述
public class SelectSort {
public static void main(String[] args) {
//给定一个数组
int[] list = {2, 56, 12, 23, 5};
seclectSort(list);
System.out.println(Arrays.toString(list));
}
public static void seclectSort(int[] list) {
//从小到大排列
for (int i = 0; i < list.length - 1; i++) {
//假设每次循环时,第一个数是最小值
int minIndex = i;
int min = list[i];
//从下标为i + 1往后开始遍历,找最小值
for (int j = i + 1; j < list.length; j++) {
//用假定的最小值依次和后面的值进行比较
//比它还小则交换,记录下该值及该值的下标
if(min > list[j]) {
min = list[j];
minIndex = j;
}
}
if(minIndex != i) {
//将最小值放在前面,也就是进行交换
list[minIndex] = list[i];
list[i] = min;
}
}
}
}
二、插入排序
算法描述
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
算法基本思想
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
算法的思路图
算法的代码描述
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
//给定一个数组,将其进行选择排序,默认升序
int[] arr = {15, 25, 3, 10, 9};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertSort(int[] arr) {
int insertVal = 0;//定义一个临时变量用于存放带插入的数
int insertIndex = 0;
for (int i = 1; i < arr.length; i++) {
//定义带插入的数
insertVal = arr[i];
insertIndex = i - 1;//即arr[i]前面那个数的下标
//现在要给insertVal找插入的位置
//insertIndex >= 0是保证在插入过程中,数组的下标不会越界
//insertVal < arr[insertIndex]则表示待插入的数,还没找到插入位置
//找到插入位置后,要先将arr[insertIndex]后移,空出位置,才能进行下一步的插入
while(insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex --;
}
//退出while循环表示已经找到了要插入的位置:insertIndex + 1
//判断是否可以进行插入赋值
if(insertIndex + 1 != i) {
arr[insertIndex + 1] = insertVal;
}
}
}
}
三、冒泡排序
算法描述
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
算法的思路分析
- 如果是n个数的话,一共要进行n-1次排序;
- 每进行完一趟排序后,他的下一趟的排序次数会减少一次;
- 排序时,若两个元素为逆序(默认从小到大的升序顺序),则进行交换;
- 设置标志位flag,如果发生了交换flag设置为1;如果没有交换就设置为-1。这样当一轮比较结束后如果flag仍为-1,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。
算法的源代码描述
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
//给定一个数组,将其从小到大进行排序
int[] arr = {12, 23, 5, 11, 2};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
int temp = 0;//定义一个临时变量,用于进行两数的交换操作
int flag = -1;//设置一个标志量,用于检测是否进行了一轮排序
for (int i = 0; i < arr.length - 1; i++) {
//外层循环表示所要进行的趟数,有n个数的话,要进行n-1趟排序
for (int j = 0; j < arr.length - 1 - i; j++) {
//内存循环则进行的是排序过程,每进行一趟排序,他的下一趟排序的次数都会少一次
//若是逆序,就进行两数的交换
if(arr[j] > arr[j + 1]) {
flag = 1;//若进行交换,就给标志量置1
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
//一趟排序进行完后,标志量的值如果为1,表示实际上是进行了一趟排序
//如果不为1,表示没进行排序
//这样的话就不用进行后面的排序了,数组已经是有序数组
if(flag != 1) {
return;
} else {
flag = -1;//每进行一趟排序之后,将标志量重置
}
}
}
}
四、快速排序
算法简单介绍
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法的描述
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
算法的思路图
算法的代码描述
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = { -9, 78, 0, 23, -567, 70};
quickSort( arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {
int l = left;//左下标
int r = right;//右下标
int mid = arr[(left + right) / 2];
int temp = 0;
//当遇到比mid小的值的话,放在mid左边;当遇到比mid大的值得话,放在mid右边
while(l < r) {
//如果在mid的左边找到>=mid的数,就会退出循环
while(arr[l] < mid) {
l ++;
}
//如果在mid的右边找到<=mid的数,就会退出循环
while(arr[r] > mid) {
r --;
}
//如果left>=right则说明mid左右两边的值,已经排好了(即mid左边的数都小于mid,mid右边的数都大于mid)
if(l >= r) {
break;
}
//交换两个数的操作
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交换完后,发现arr[left]==mid,这时候要将right前移
if(arr[l] == mid) {
r --;
}
//如果交换完后,发现arr[right]==mid,这时候要将left后移
if(arr[r] == mid) {
l ++;
}
}
//如果left==right,必须left++,right--,否则的话会出现栈的溢出
if(l == r) {
l ++;
r --;
}
//往左边递归
if(left < r) {
quickSort(arr ,left ,r);
}
//往右边递归
if(right > l) {
quickSort(arr ,l ,right);
}
}
}
五、希尔排序
算法简单介绍
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
算法的描述
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt< d(t-1)…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
算法的思路图
算法的代码描述
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] arr ={ 8, 5, 12, 9, 1, 7, 3};
shellSort1(arr);
System.out.println(Arrays.toString(arr));
System.out.println("==========================");
shellSort2(arr);
System.out.println(Arrays.toString(arr));
}
//第一种排序方法:交换排序
public static void shellSort1(int[] arr) {
int temp = 0;//定义一个临时变量,用于两数交换的操作
for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
//第一层循环用于控制步长
for (int i = gap; i < arr.length; i++) {
//第二层循环用于遍历各组中所有的元素(共有gap组,每组两个元素)
for (int j = i - gap; j >= 0 ; j -= gap) {
//如果当前元素大于加上步长后的那个元素,说明交换
if(arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
}
//第二种排序方法:移位法(类似于插入排序的思想,先一个一个找位置,将大于它的数往后移)
public static void shellSort2(int[] arr) {
for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
//定义一个增量gap表示步长,并逐步的缩小范围
for (int i = gap; i < arr.length; i++) {
//从第gap个元素开始,逐个对其所在的组进行直接插入排序
int j = i;
int temp = arr[j];
if(arr[j] < arr[j - gap]) {
while(j - gap >=0 && temp < arr[j - gap]) {
//移动
arr[j] = arr[j - gap];
j -= gap;
}
//当退出while循环后,就表示已经给temp找到了合适的位置
arr[j] = temp;
}
}
}
}
}
六、归并排序
算法简单说明
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。
算法的思路描述
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3直到某一指针超出序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾
算法的代码描述
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
//给定一个乱序的数组,将其用归并排序法进行排序
int[] arr = { 8, 2, 5, 3, 1, 4, 7, 6};
//归并排序需要一个额外的数组空间
int[] temp = new int[arr.length];
mergeSort( arr, 0, arr.length - 1, temp);
System.out.println(Arrays.toString(arr));
}
//分 + 合的方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if(left < right) {
//中间的一个索引
int mid = (left + right) / 2;
//向左递归进行分解
mergeSort( arr, left, mid, temp);
//向右递归进行分解
mergeSort( arr,mid + 1, right, temp);
//将所有的解进行合并
merge( arr, left, mid, right, temp);
}
}
//合并的方法
/**
*
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转操作的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;//初始化i,左边有序序列的初始索引
int j = mid + 1;//初始化j,右边有序序列的初始索引
int t = 0;//指向temp数组的当前索引
//1.先把左右两边的有序的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完为止
while(i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,填充到temp数组
//t 和 i都向后移一位
if(arr[i] <= arr[j]) {
temp[t] = arr[i];
t ++;
i ++;
} else {
//反之则将右边有序序列的当前元素,填充到temp数组
temp[t] = arr[j];
t ++;
j ++;
}
}
//2.把有剩余数据的一边剩下的数,挨个依次填充到temp
while(i <= mid) {
//若左边有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[i];
t ++;
i ++;
}
while(j <= right) {
//若右边有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[j];
t ++;
j ++;
}
//3.将temp数组的元素拷贝到arr数组中
//【注意】并不是每次都拷贝所有的数
t = 0;
int tempLeft = left;//定义一个临时变量,用于储存左边索引的下标
//第一次合并tempLeft = 0,right = 1;第二次tempLeft = 2 ,right = 3 ;第三次tempLeft = 0, right = 3;
//合并完左边的数组后,合并右边的数组
//最后一次tempLeft = 0 , right= 7
while(tempLeft <= right) {
arr[tempLeft] = temp[t];
t ++;
tempLeft ++;
}
}
}
七、堆排序
代码实现
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = { 4, 6, 8, 5, 9};
heapSort(arr);
}
//写一个堆排序的方法
public static void heapSort(int[] arr) {
int temp = 0;
System.out.println("进行堆排序!!!");
//将无序序列构建成一个堆,根据升序降序需求来选择大顶堆还是小顶堆
for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
adjustHeap( arr, i, arr.length);
}
//将堆顶元素与末尾元素交换,将最大元素“沉”到数组的最末端
//重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
for (int j = arr.length - 1; j > 0 ; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap( arr, 0, j);//每次需要调整的数组的大小在减小
}
System.out.println("排序后的数组为:" + Arrays.toString(arr));
}
//将一个数组(二叉树),调整成一个大顶堆(局部的调整)===> 以结点i为根的子树的调整
/**
* 功能:完成将以i对应的非叶子节点的树调整成大顶堆
* 举例:int[] arr = { 4, 6, 8, 5, 9}===> i=1 ===> adjustHeap ===> 得到{ 4, 9, 8, 5, 6}
* 如果再次调用adjustHeap,adjustHeap传入的是 i=0 ===> 原来的{ 4, 9, 8, 5, 6}会变为{ 9, 9, 8, 5, 4}
* @param arr 待调整的数组
* @param i 表示非叶子节点在数组中的索引
* @param length 表示对多少个元素继续调整,length是在逐渐的减少
*/
public static void adjustHeap( int[] arr, int i, int length) {
int temp = arr[i];//先取出当前元素的值,保存在临时变量里
//k=2*i+1是i节点的左子结点
for (int j = 2 * i + 1; j < length; j = 2 * j + 1) {
if(j + 1 < length && arr[j] < arr[j + 1]) {
//说明左子结点的值小于右子节点的值
j ++;//j指向右子节点
}
if(arr[j] > temp) {
//如果子节点大于父节点
arr[i] = arr[j];//把较大的值赋给当前结点
i = j;//!!!i指向k,继续循环比较
} else {
break;
}
}
//当循环结束后,我们已经将以i为父节点的树的最大值,放在了该子树的最顶端(局部的调整)
arr[i] = temp;//将temp值放到调整后的位置
}
}