生病在床上养了这么久终于病好了,重新开始搞学习!!!
1.优先队列
优先队列分为最大优先队列和最小优先队列
最大优先队列:每次出队的永远是当前队列中最大的元素
最小优先队列:每次出队的永远是当前队列中最小的元素
实现:利用之前二叉堆的性质,堆顶元素永远是整个堆中最大(最小)的元素,那实现优先队列只需要每次将堆顶元素出队并删除就行了。
以最大优先队列为例
package 优先队列;
import java.util.Arrays;
//最大优先队列:每次出队都是队列中最大元素;
//利用二叉堆实现优先队列,入队就是堆的插入操作,出队就是堆顶的删除操作
public class priorityqueue {
private int[] array;
private int size;
public priorityqueue(){array=new int[32];}
//最大优先队列入队操作
public void enqueue(int key)
{
if(size>=array.length)
{
resize();
}
array[size++]=key;
upadjust();
}
//最大优先队列出队操作
public int dequeue() throws Exception
{
if(size<=0)
{
throw new Exception("队列为空!");
}
int head=array[0];
array[0]=array[--size];
downadjust();
return head;
}
//下沉调整
private void downadjust()
{
int parentindex=0;
int temp=array[parentindex];
int childindex=1;
while(childindex<size)
{
if(childindex+1<size&&array[childindex+1]>array[childindex])
{
childindex++;
}
if(temp>=array[childindex])//最小堆或者最小优先队列这里是小于等于
break;
array[parentindex]=array[childindex];
parentindex=childindex;
childindex=2*childindex+1;
}
array[parentindex]=temp;
}
//上浮调整
private void upadjust()
{
int childindex=size-1;
int parentindex=(childindex-1)/2;
int temp=array[childindex];
while(childindex>0&&temp>array[parentindex])//最小堆或者最小优先队列这里是小于
{
array[childindex]=array[parentindex];
childindex=parentindex;
parentindex=(parentindex-1)/2;
}
array[childindex]=temp;
}
//扩容
private void resize()
{
int newsize=this.size*2;//每次扩大为原来的两倍
this.array= Arrays.copyOf(this.array,newsize);
}
public static void main(String[] args) throws Exception {
priorityqueue pqueue=new priorityqueue();
pqueue.enqueue(3);
pqueue.enqueue(5);
pqueue.enqueue(10);
pqueue.enqueue(2);
pqueue.enqueue(7);
pqueue.enqueue(1);
pqueue.enqueue(18);
int size=pqueue.size;
for(int i=0;i<size;i++)
{
System.out.printf("最大优先队列的出队的第%d个元素为%d\n",i+1,pqueue.dequeue());
}
}
}
运行结果
2.冒泡排序
冒泡排序是最基本最基本的排序了,还记得当时学C语言做那个学生管理系统排序每次就是冒泡啊冒泡的,真的是太基础了。每次别人问冒泡怎么写,回答永远是就写两个for循环啊。这也就说明它的时间复杂度是O(n^2),最简单的思想呢就是把相邻的两个元素都比较一下,按照自己想要的顺序致定规则交换就可以了。
实现升序排序
public static void sort(int array[])
{
for(int i=0;i<array.length;i++)
{
for(int j=0;j<array.length-i-1;j++)
{
if(array[j]>array[j+1])
{
array[j]=array[j]+array[j+1];
array[j+1]=array[j]-array[j+1];
array[j]=array[j]-array[j+1];
}
}
}
}
如果我们经过length-n轮的时候数列就已经是有序数列的时候,那我们后续的几轮排序就是多余的了,为此,我们可以设置一个标记让排序可以及时停止。
//改进版冒泡排序
public static void sort_plus(int array[])
{
for(int i=0;i<array.length;i++)
{
boolean issorted=true;//是否有序的标记
for(int j=0;j<array.length-i-1;j++)
{
if(array[j]>array[j+1])
{
array[j]=array[j]+array[j+1];
array[j+1]=array[j]-array[j+1];
array[j]=array[j]-array[j+1];
issorted=false;//无序就是改为false
}
}
if(issorted)//如果是有序就提前停止排序
break;
}
}
再假如我们一开始得到的数列就已经是有部分是有序的,那我们每一轮也不用比较那些已经是有序的部分了。所以我们可以在每一次比较交换时将最后一次交换元素的位置作为标志,那么后面的部分就是我们的有序区,不用每次重复去比较这部分。
//再改进版冒泡排序
public static void sort_pp(int array[])
{
int lastchange=0;
int sortborder=array.length-1;//有序区的标志下标
for(int i=0;i<array.length-1;i++)
{
boolean issorted=true;
for(int j=0;j<sortborder;j++)//循环到有序区就停止
{
if(array[j]>array[j+1])
{
array[j]=array[j]+array[j+1];
array[j+1]=array[j]-array[j+1];
array[j]=array[j]-array[j+1];
issorted=false;
lastchange=j;
}
}
sortborder=lastchange;
if(issorted)
break;
}
}
最强的冒泡!!!双向来比较交换位置,以求最少次数的循环实现排序。思想很简单,单数轮从左往右循环比较,偶数轮从右往左循环比较,这种排序也叫鸡尾酒排序。
public static void jwsort(int array[])
{
for(int i=0;i<array.length/2;i++)
{
boolean issorted=true;
//奇数次轮
for(int j=i;j<array.length-i-1;j++)
{
if(array[j]>array[j+1])
{
array[j]=array[j]+array[j+1];
array[j+1]=array[j]-array[j+1];
array[j]=array[j]-array[j+1];
issorted=false;
}
}
if(issorted)
break;
//偶数次轮
issorted=true;
for(int j=array.length-1-i;j>i;j--)
{
if(array[j]<array[j-1])
{
array[j]=array[j]+array[j-1];
array[j-1]=array[j]-array[j-1];
array[j]=array[j]-array[j-1];
issorted=false;
}
}
if(issorted)
break;
}
}
那最后就来看看运行的时间
确实鸡尾酒排序的效率高了很多。
总结
冒泡排序如果原数列大部分有序那鸡尾酒排序的效率最高,如果一开始很无序的话可以用第二种冒泡排序或者用最基本的冒泡排序。反正冒泡记住一句话,i从0-length-1,j从0-length-i-1,以后千万别再问别人冒泡怎么写了鸭哈哈哈