冒泡排序
算法规则: 由于算法每次都将一个最大的元素往上冒,我们可以将待排序集合(0...n)看成两部分,一部分为(k..n)的待排序unsorted集合,另一部分为(0...k)的已排序sorted集合,每一次都在unsorted集合从前往后遍历,选出一个数,如果这个数比其后面的数大,则进行交换。完成一轮之后,就肯定能将这一轮unsorted集合中最大的数移动到集合的最后,并且将这个数从unsorted中删除,移入sorted中。冒泡排序的时间复杂度为O(n^2)。
代码实现(java)
public void bubbleSort(int[] data){
//这里从数组最后面开始遍历
for (int i = data.length - 1; i > 0; --i) {
//在这里体现出 “将每一趟排序选出来的最大的数从sorted中移除”
for (int j = 0; j < i; j++) {
//保证在相邻的两个数中比较选出最大的并且进行交换(冒泡过程)
if (data[j] > data[j + 1]) {
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
并归排序
- 算法规则: 像快速排序一样,由于归并排序也是分治算法,因此可使用分治思想:
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3直到某一指针到达序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾
空间复杂度为O(n),时间复杂度为O(nlogn)。 - 代码实现(java)
public void mergeSort(int[] a, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
//左边
mergeSort(a, low, mid);
//右边
mergeSort(a, mid + 1, high);
merge(a, low, mid, high);
}
}
private void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
//指向左边的指针
int i = low;
//指向右边的指针
int j = mid + 1;
int k = 0;
//把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (a[i] > a[j]) {
temp[k++] = a[j++];
} else {
temp[k++] = a[i++];
}
}
//左边剩余的数据加入得到新数组
while (i <= mid) {
temp[k++] = a[i++];
}
//右边剩余的数据加入到新数组
while (j <= high) {
temp[k++] = a[j++];
}
for (int l = 0; l < temp.length; l++) {
a[l + low] = temp[l];
}
}
快速排序
算法规则: 本质来说,快速排序的过程就是不断地将无序元素集递归分割,一直到所有的分区只包含一个元素为止。
由于快速排序是一种分治算法,我们可以用分治思想将快排分为三个步骤:
1.分:设定一个分割值,并根据它将数据分为两部分
2.治:分别在两部分用递归的方式,继续使用快速排序法
3.合:对分割的部分排序直到完成
快速排序是不稳定的,其时间平均时间复杂度是O(nlgn)。代码实现(java)
public static int dividerAndChange(int[] args, int start, int end) {
//参照值
int pivot = args[start];
while (start < end) {
//首先从右向左查找,直到找到比参数小的第一个数,然后交换位置
while (start < end && pivot <= args[end]) {
end--;
}
if (start < end) {
//开始交换位置
args[start] = args[end];
start++;
}
//从左往右找,找到比参数大的就交换位置
while (start < end && pivot > args[start]) {
start++;
}
if (start < end) {
//开始交换位置
args[end] = args[start];
end--;
}
}
args[start] = pivot;
System.out.println("start:" + start + ",end:" + end);
return start;
}
public static void quickSort(int[] args, int start, int end) {
if (end - start > 1) {
int mid = dividerAndChange(args, start, end);
//对左边数组排序
quickSort(args, start, mid);
//对右边数组排序
quickSort(args, mid + 1, end);
}
}
选择排序
算法规则: 将待排序集合(0...n)看成两部分,在起始状态中,一部分为(k..n)的待排序unsorted集合,另一部分为(0...k)的已排序sorted集合,在待排序集合中挑选出最小元素并且记录下标i,若该下标不等于k,那么 unsorted[i] 与 sorted[k]交换 ,一直重复这个过程,直到unsorted集合中元素为空为止。选择排序的时间复杂度为O(n^2)
代码实现(java)
public void selectionSort(int[] args){
for (int i = 0; i < args.length - 1; i++) {
int k = i;
for (int j = k + 1; j < args.length; j++) {
//循环遍历,找到最小值的下标
if (args[j] < args[k]) {
k = j;
}
}
if (k != i) {
//交换args[i]和args[k]
int temp = args[i];
args[i] = args[k];
args[k] = temp;
}
}
}
插入排序
- 算法规则:插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的的。相信大家都有过打扑克牌的经历,特别是牌数较大的。在分牌时可能要整理自己的牌,牌多的时候怎么整理呢?就是拿到一张牌,找到一个合适的位置插入。这个原理其实和插入排序是一样的。举个栗子,对5,3,8,6,4这个无序序列进行简单插入排序,首先假设第一个数的位置时正确的,想一下在拿到第一张牌的时候,没必要整理。然后3要插到5前面,把5后移一位,变成3,5,8,6,4.想一下整理牌的时候应该也是这样吧。然后8不用动,6插在8前面,8后移一位,4插在5前面,从5开始都向后移一位。注意在插入一个数的时候要保证这个数前面的数已经有序。简单插入排序的时间复杂度也是O(n^2)。
/**
* 插入排序
* @param args
* @return
*/
public static int[] insertSort(int[] args) {
if (args.length == 0) {
return null;
}
// 默认数组的第一个数字已经排好序
// 这里把第二个数字,作为拿来比较的数字.假设第一个数字位置已经确定.然后将二个数字和第一个数字比较
for (int i = 1; i < args.length; i++) {
int k = i;
//这里将未排序的第一个数字拿出来
int target = args[i];
/*for (k = i - 1; k >= 0 && target < args[k]; k--) {
args[k + 1] = args[k];
}*/
//这里循环比较,将未排序的第一个数和前面已排序的数组循环比较
while (k >= 0 && target < args[k - 1]) {
args[k] = args[k - 1];
k--;
}
//这里重新设置参照值.
args[k] = target;
}
return args;
}