部分代码的Github地址为:github.com/hzka/JavaBo…
(一)基础知识
1.插入排序:
1.1算法思想:
当插入第i(i>=1)个元素时,前面的V[0],…,V[i-1]等i-1个 元素已经有序。这时,将第i个元素与前i-1个元素V[i-1],…,V[0]依次比较,找到插入位置即将V[i]插入,同时原来位置上的元素向后顺移。在这里,插入位置的查找是顺序查找。直接插入排序是一种稳定的排序算法。时间复杂度为O(n^2)。
1.2比较步骤:
****
1.3代码实现:
private static int[] InsetSort(int[] lists) {
//1.开始遍历
for (int i = 1; i < lists.length; i++) {
//2.保存当前排序节点
int currentElement = lists[i];
int k;
//3.从当前节点的上一节点开始检查,如果当前节点大于等于零,且上一节点的值大于当前节点的值,赋值。
for(k=i-1;k>=0 && lists[k]>currentElement;k--){
lists[k+1] = lists[k];
}
//4.否则,对于lists[k+1]进行赋值
lists[k+1] = currentElement;
}
//5.返回数组。
return lists;
}
2.冒泡排序:
2.1算法思想
(1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
(2)对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
(3)针对所有的元素重复以上的步骤,除了最后一个。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
最佳情况O(n),最差情况O(n^2)。
2.2比较步骤
****
2.3代码示例
private static int[] BubbleSort(int[] lists) {
//1.若一次循环没有发生交换,那么后面的也就不用做了。
boolean nedNextPass = true;
//2.双重循环遍历,进行交换。确定从小到大的顺序。
for (int k = 0; k < lists.length && nedNextPass; k++) {
nedNextPass =false;
for (int i = k; i < lists.length - 1; i++) {
int j = i + 1;
if (lists[i] > lists[j]) {
int tmp = lists[j];
lists[j] = lists[i];
lists[i] = tmp;
nedNextPass =true;
}
}
}
return lists;
}
3.归并排序
3.1算法思想:
(1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
(2)设定两个指针,最初位置分别为两个已经排序序列的起始位置
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
(4)重复步骤3直到某一指针超出序列尾
(5)将另一序列剩下的所有元素直接复制到合并序列尾
归并排序是稳定的排序.归并时间复杂度为O(nlogn),由于插入、选择和冒泡。
3.2比较步骤
3.3代码示例
private static void merageSort(int[] lists) {
if(lists.length>1){
//1.拆分前面一半
int [] firstHalf = new int[lists.length/2];
System.arraycopy(lists,0,firstHalf,0,lists.length/2);
merageSort(firstHalf);
//2.拆分前面一半
int secondHalfLength = lists.length - lists.length/2;
int [] secondHalf = new int[secondHalfLength];
System.arraycopy(lists,lists.length/2,secondHalf,0,secondHalfLength);
merageSort(secondHalf);
//3.合并
merage(firstHalf,secondHalf,lists);
}
}
private static void merage(int[] list1, int[] list2, int[] temp) {
int current1 = 0;//list1开始位置
int current2 = 0;//list2开始位置
int current3 = 0;//list3开始位置
//比较累加填充,有意思。
while (current1<list1.length && current2<list2.length){
if(list1[current1]<list2[current2]){
temp[current3++] = list1[current1++];
}else{
temp[current3++] = list2[current2++];
}
}
while (current1<list1.length)
temp[current3++] = list1[current1++];
while (current2<list2.length)
temp[current3++] = list2[current2++];
}
4.快速排序
4.1算法思想:
一趟快速排序:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
5)重复第3、4步,直到i=j;3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。时间复杂度为:O(n) =O(nlogn)
链接:jingyan.baidu.com/article/d45… www.cnblogs.com/surgewong/p…
4.2比较步骤: (好像比较方法有很多种,故依次排序后情况可能不一样。)
4.3代码示例:
private static void quick_sort(int s[], int l, int r) {
if (l < r) {
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j) {
while (i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if (i < j)
s[i++] = s[j];
while (i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if (i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
5.堆排序
5.1算法思想:
二叉堆:(1)完全二叉树;(2)每个节点大于等于它的任意一个孩子。堆及堆的表示、添加新节点、删除新节点以及代码参考我的上一个帖子:blog.csdn.net/weixin_3824…
堆的时间复杂度:O(nlogn),相比于归并排序,堆排序不需要额外空间。
5.2代码示例:
public class Heap {
private ArrayList list = new ArrayList();
public Heap() {
}
public Heap(Object[] objects) {
for (int i = 0; i < objects.length; i++) {
add(objects[i]);
}
}
public void add(Object newobject) {
//1.先在队尾添加新结点。
list.add(newobject);
//2.确定当前的下标。更新结点的值。原理很简单,只是Arraylist的简单拓展。
int currentIndex = list.size() - 1;
while (currentIndex > 0) {
int parentIndex = (currentIndex - 1) / 2;
if (((Comparable) (list.get(currentIndex))).compareTo(list.get(parentIndex)) > 0) {
Object temp = list.get(currentIndex);
list.set(currentIndex, list.get(parentIndex));
list.set(parentIndex, temp);
} else break;
currentIndex = parentIndex;
}
}
public Object remove() {
if (list.size() == 0) return null;
//1.获取第零个元素,将最后一个元素放在第零个元素处。然后移除掉最后一个元素。
Object removeObject = list.get(0);
list.set(0, list.get(list.size() - 1));
list.remove(list.size() - 1);
int currentIndex = 0;
while (currentIndex < list.size() - 1) {
int leftchildIndex = currentIndex * 2 + 1;
int rightchildIndex = currentIndex * 2 + 2;
//2.判断准备替换哪一个结点,替换左孩子结点还是右孩子节点。
if (leftchildIndex >= list.size()) break;
int maxIndex = leftchildIndex;
if (rightchildIndex < list.size()) {
if (((Comparable) (list.get(maxIndex))).compareTo(list.get(rightchildIndex)) < 0) {
maxIndex = rightchildIndex;
}
}
//3.和左右节点的最大值进行交换。
if (((Comparable) (list.get(currentIndex))).compareTo(list.get(maxIndex)) < 0) {
Object temp = list.get(maxIndex);
list.set(maxIndex,list.get(currentIndex));
list.set(currentIndex,temp);
currentIndex = maxIndex;
}else{break;}
}
return removeObject;
}
public int getSize(){
return list.size();
}
}
6.选择排序:
6.1算法思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。
6.2比较步骤:
****
6.3代码实现:
public static int[] SimpleSelctSort(int []lists){
for(int i= 0;i<lists.length-1;i++){
int min = i;
for(int j = i+1;j<lists.length;j++){
if(lists[j] < lists[min]){
min = j;
}
}
int tmp = lists[i];
lists[i] = lists[min];
lists[min] = tmp;
}
return lists;
}
7.希尔排序:
7.1算法思想:
设待排序元素序列有n个元素,首先取一个整数increment(小于n)作为间隔将全部元素分为increment个子序列,所有距离为increment的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔increment,重复上述子序列划分和排序工作。直到最后取increment=1,将所有元素放在同一个子序列中排序为止。
那么增量该如何取?一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
参考链接:blog.csdn.net/weixin_3781…
7.2比较步骤:
****
7.3代码实现:
public static void sort(int[] arrays) {
if (arrays == null || arrays.length <= 1) {
return;
}
//增量
int incrementNum = arrays.length / 2;
while (incrementNum >= 1) {
for (int i = 0; i < arrays.length; i++) {
//进行插入排序
for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) {
if (arrays[j] > arrays[j + incrementNum]) {
int temple = arrays[j];
arrays[j] = arrays[j + incrementNum];
arrays[j + incrementNum] = temple;
}
}
}
//设置新的增量
incrementNum = incrementNum / 2;
}
}
8.桶排序与基数排序
对整数排序的高效算法。
8.1桶排序
(1)设置一个定量的数组当作空桶子。
(2)寻访序列,并且把项目一个一个放到对应的桶子去。
(3)对每个不是空的桶子进行排序。
(4)从不是空的桶子里把项目再放回原来的序列中。
参考链接:blog.csdn.net/developer10…
8.2基数排序
一共只有十个桶,反向放置遍历。
9.外部排序:
用途: 主要用作对大容量数据进行排序。
算法思想:
外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行多路归并排序。先拆分和内存大小的文件,载入进行内部排序,将有序的子文件重新写入外存,对这些有序子文件进行逐趟归并,使其逐渐由小到大,直至得到整个有序文件为止。
链接:blog.csdn.net/jeason29/ar…
外部排序容易出现的问题及相应的解决方法: 1.缓存。加快数据读入到内存中。 2.流水线。将排序过程分为,读取数据→排序数据→写数据。3条线程并行处理。所以你在将数据分解到每一个文件中的时候,确保每个文件的大小最好是所给内存的1/3。 3.堆排序。根据堆排序的插入logn以及返回最小值o(1)。单个文件排序用快排,最后的外部排序用堆排序。
(二)总结:
****
补充:
1.所有排序算法的时间复杂度及稳定与否?
所谓排序算法稳定性,即假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
记住:希快选堆不稳定(希尔排序、快速排序、选择排序以及堆排序)
此图原图链接为:blog.csdn.net/weixin_3859….
2.什么时候该用什么样的排序算法?
1. 冒泡,插入,选择三种排序中,当数据量很大时,选择排序性能会更好;
2. 堆排,希尔,归并,快排几种排序算法也表现不错,源于其时间复杂度达到了O(nlogn)O(nlogn);
3. 随机快速排序性能确实表现十分亮眼,甚至有时比基数排序和桶排序还好。
4.插入排序适用于少量数据的排序,其时间复杂度为O(n^2)。
5.二分排序则是利用了数组能够快速定位的特点,时间复杂度也是O(n^2),适用于较大数据排序
6.希尔排序的实质是分组再进行直接插入,开始时增量较大时,插入排序的数量少,所以前期会很快。当增量变小时,数据也基本有序了。所以希尔排序的时间复杂度要比O(n^2)要好很多。
参考链接:www.cnblogs.com/luoahong/p/…