Java与算法一.docx
《Java与算法一.docx》由会员分享,可在线阅读,更多相关《Java与算法一.docx(21页珍藏版)》请在冰豆网上搜索。
Java与算法一
Java与算法
(一)
一冒泡排序
冒泡排序法的原理是,每次比较相邻的两个元素,如果它们的顺序错误就把它们交换过来。
例如对4362715这7个数字进行从小到大的排序,从最左侧开始,首先比较4和3
例如对4362715这7个数字进行从小到大的排序,从最左侧开始,首先比较4和3
因为是从小到大排序,4和3的顺序显然是错误的,交换他们,得到
接下来比较4和6
顺序是正确的,不需要任何操作。
接下来进行下一步,比较6和2
6显然应该排在2的后面,怎么办?
交换它们,得到
经过前面几步,已经可以总结出规律,那么接下来要做的比较依次是:
7>1?
得到3426175
7>5?
得到
到此,7的右边已经没有数可以比较,第一轮排队结束。
经过这一轮,已经成功的把最大的数,即7放在了最后。
但是前面6个数的顺序还是不对,但是按照上面的思路很容易想到,对前面6个数再来一遍,即可把6放到倒数第二的位置。
然后再对前面5个数重复逐个比较的步骤。
。
。
7个数字需要进行7-1=6次排队,每完成一轮排队,下一轮排队需要比较的数字个数减1,来看代码
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicclassBubbleSort{
publicvoidsort(int...numbers){
//n个数执行n-1轮
//每一轮后完成一个数字归位,下一轮要比较的数字个数减1(所以内层循环是jintn=numbers.length-1;
intt;
for(inti=0;ifor(intj=0;jif(numbers[j]>numbers[j+1]){
t=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=t;
}
}
}
}
}
测试
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicstaticvoidmain(String[]args){
int[]numbers=newint[]{4,3,6,2,7,1,5};
System.out.print("before:
");
for(inti=0;iSystem.out.print(numbers[i]+"");
}
System.out.println();
newBubbleSort().sort(numbers);
System.out.print("after:
");
for(inti=0;iSystem.out.print(numbers[i]+"");
}
System.out.println();
}
输出
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
before:
4362715
after:
1234567
冒泡排序的核心是两层嵌套的循环,时间复杂度是O(N^2),即对N个数排序,需要近似执行N的平方次。
因为效率较低,实际开发中基本不会使用,但是因为简单易懂通常做为学习算法的入门案例。
如果用上面的代码对1234567做从小到大排列,会发现虽然数字已经排列好,但是程序还是要忠实的执行完全部两层循环。
对这种情况,我们可以引入一个变量来记录一次内层循环中交换数字的个数,如果交换个数为0,则提前终止循环,在某些情况下可以提高效率。
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicvoidbetterSort(booleandescend,int...numbers){
System.out.print("before:
");
for(inti=0;iSystem.out.print(numbers[i]+"");
}
System.out.println();
//n个数执行n-1轮
//每一轮后完成一个数字归位,下一轮要比较的数字个数减1(所以内层循环是jintn=numbers.length-1;
intt;
intflag=0;
for(inti=0;ifor(intj=0;jif(descend){//从大到小
if(numbers[j]t=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=t;
flag=1;
}
}else{
if(numbers[j]>numbers[j+1]){
t=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=t;
flag=1;
}
}
}
if(flag==0){
break;
}else{
flag=0;
}
}
System.out.print("after:
");
for(inti=0;iSystem.out.print(numbers[i]+"");
}
System.out.println();
}
增加一个变量需要额外占用内存空间,因此,这个方法是以空间换时间。
二快速排序
快速排序的基本思路是,每次选定数列中的一个基准数,将小于基准数的数字都放到基准数左边,大于基准数的数字都放到基准数右边。
然后再分别对基准数左右的两个数列分别重复以上过程。
仍以4362715为例。
选定最左侧数字4为基准数,首先从右开始向左找小于4的数,找到第一个数1后停止。
然后从左开始向右找到第一个大于4的数,即6。
交换这两个数的位置,得到
继续寻找,仍然从右边开始,从上一步找到1的位置向左寻找小于4的数,找到2停止。
然后从左边找到6的位置向右找大于4的数。
右移一格后,和右侧来的“探路者”相遇了,这意味着这一轮排序结束。
最后把结束位置的数和基准数交换
观察完成后的数列,可以看到以基准数4为分界线,左边的数全都比4小,右边的数全都比4大。
接下来分别对左边的231和右边的765重复上面的排序步骤。
231
以2为基准数->213->123
765
以7为基准数->567
我们例子中的数字较少,如果数列足够长,对第一次排序后得到的子数列排序,将再得到两个子数列,然后再一分为二、二分为四。
。
。
直到以基准数拆分后两边都只剩下一个数字。
首先来看递归形式的实现代码
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicclassQuickSort{
publicvoidsort(intleft,intright,int...numbers){
if(left>=right){
return;
}
inttemp=numbers[left];
intt=0;
inti=left;
intj=right;
while(i!
=j){
//先从右往左找
while(numbers[j]>=temp&&ij--;
//再从左往右找
while(numbers[i]<=temp&&ii++;
//交换两个数在数组中的位置
if(it=numbers[i];
numbers[i]=numbers[j];
numbers[j]=t;
}
}
//将基准数归位
numbers[left]=numbers[i];
numbers[i]=temp;
sort(left,i-1,numbers);
sort(i+1,right,numbers);
}
}
测试代码
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicstaticvoidmain(String[]args){
int[]numbers=newint[]{4,3,6,2,7,1,5};
newQuickSort().sort(0,numbers.length-1,numbers);
System.out.print("after:
");
for(inti=0;iSystem.out.print(numbers[i]+"");
}
System.out.println();
}
输出
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
after:
1234567
另一种实现方式是使用栈代替递归
[java]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
publicvoidsortWithoutRecursion(intleft,intright,int...numbers){
LinkedListstack=newLinkedList<>();
intindex;
stack.push(left);
stack.push(right);
while(!
stack.isEmpty()){
right=stack.pop();
left=stack.pop();
index=partition(left,right,numbers);
if(leftstack.push(left);
stack.push(index-1);
}
if(right>index+1){
stack.push(index+1);
stack.push(right);
}
}
}
publicintpartition(intleft,intright,int...numbers){
inttemp