您的位置:首页 > 编程语言 > Java开发

java基本算法之排序

2015-07-24 20:19 337 查看
注:这个是以前写的,主要讲的是算法的思路,此文章的归并排序我并没有给出,因为当时还不会,而且没有基数排序和计数排序,最近重新学习了一遍,想要看的同学可以看我的另一篇文章:

/article/10537371.html

被数据结构虐的狗血淋头,于是乎要重头整理一遍,巩固一下知识块。以下算法的讲解均来自百度百科,代码是我自己打的,因为自己表述的可能不太清楚。。。

第一个:冒泡排序:重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

代码演示

private int[] bubbertSort(int[] a) {
for(int i=0;i<a.length;i++)
for(int j=0;j<a.length;j++)
if (a[j]<a[i]) {
int temp=a[j];
a[j]=a[i];
a[i]=temp;

}


第二个:直接插入排序:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。

第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。

直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。

直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置。直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较,直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。

代码演示

private int[] directsSort(int[] a){
int temp=0,flag=0;
for (int i = 1; i < a.length; i++)
{
int j=i-1;
temp=a[i];
//一定是要j>=0不能是j>0,否则第一个比较会跳过
for(;j>=0&&temp<a[j];j--){
a[j+1]=a[j];
}
a[j+1]=temp;
}
return a;
}


第三个,简单选择排序,即依遍历得到a
中的最小值,然后进行排序的过程,遍历依次得到a[1]-a
最小值与[1]交换,然后遍历a[2]-a
最小值和a[2]交换,以此类推。

代码演示

private int[] simpleSort(int[] a) {
int flag;
for (int i = 0; i < a.length; i++) {
flag=i;
for (int j = i+1; j < a.length; j++) {
if (a[j]<a[flag]) {
flag=j;
}
}
if (i!=flag) {
int temp=a[flag];
a[flag]=a[i];
a[i]=temp;
}
}
return a;
}


第四个:希尔排序

希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

private int[] shellSort(int[] a) {
//d为增量,每次取一半,直到等于1
int i,j,d=0;
for (d = a.length/2; d>0; d/=2) {
for (i = 0; i < a.length; i++) {
for(j=i-d;j>=0&&a[j]>a[j+d];j-=d){
int temp=a[j];
a[j]=a[j+d];
a[j+d]=temp;
}
}

}


方法二:

public static void  main(String[] args) {
int[] a =new int[20];
for (int i = 0; i < a.length; i++) {
a[i]=(int) (Math.random()*100);
}
int d=a.length/2;
while (true) {
for (int i = 0; i < d; i++) {
for(int j=i;j+d<a.length;j+=d){
int temp;
if (a[j]>a[j+d]) {
temp=a[j];
a[j]=a[j+d];
a[j+d]=temp;
}
}

}
if (d==1) {
break;

}
d--;

}
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}


第五个:快速排序

设要排序的数组是A[0]……A[N-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-完成的时候,此时令循环结束)。

算法演示:

这个是网上的写法,目前还不太会,记录一下,以后再继续研究!!!!

private void quickSort(int[] a,int low,int high) {
if(low >= high)
{
return;
}
int first = low;
int last = high;
int key = a[first];/*用字表的第一个记录作为枢轴*/

while(first < last)
{
while(first < last && a[last] >= key)
{
last--;
}

a[first] = a[last];/*将比第一个小的移到低端*/

while(first < last && a[first] <= key)
{
first++;
}

a[last] = a[first];    /*将比第一个大的移到高端*/
}
a[first] = key;/*枢轴记录到位*/
quickSort(a, low, first-1);
quickSort(a, first+1, high);

}


第六个:堆排序:(判断是否是堆可以判断是否是完全二叉树)

n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):

(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点

若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:

树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示。

大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。

待补充


第七个:归并排序(二路归并排序)

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

public class MergeSort {
/**
* 归并排序
* 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
* 时间复杂度为O(nlogn)
* 稳定排序方式
* @param nums 待排序数组
* @return 输出有序数组
*/
public static int[] sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
sort(nums, low, mid);
// 右边
sort(nums, mid + 1, high);
// 左右归并
merge(nums, low, mid, high);
}
return nums;
}

public static void merge(int[] nums, 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 (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}

// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = nums[i++];
}

// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = nums[j++];
}

// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}

// 归并排序的实现
public static void main(String[] args) {

int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };

MergeSort.sort(nums, 0, nums.length-1);
System.out.println(Arrays.toString(nums));
}
}


各个排序方法比较:

排序方法 平均时间 最坏时间 辅助存储

简单排序 O(n2) O(n2) O(1)

快速排序 O(nlogn) O(n2) O(logn)

堆排序 O(nlogn) O(nlogn) O(1)

归并排序 O(nlogn) O(nlogn) O(n)

基数排序 O(d(n+rd)) O(d(n+rd)) O(rd)

PS:直接插入排序、冒泡排序为简单排序

希尔排序、堆排序、快速排序为不稳定排序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: