几种常用排序算法和图论

来源:互联网 发布:宋代权知开封府事 编辑:程序博客网 时间:2024/06/01 11:23

这段时间在学算法,总结一下几种常用的排序算法

1,冒泡排序算法

此算法算是入门排序算法,

public void sort(int[] a)
    {
        int temp = 0;
        for (int i = a.length - 1; i > 0; --i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (a[j + 1] < a[j])
                {
                    temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                }
            }
        }
    }
用于小数据量排序,由于交换次数过多,效率并不高

2,快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序(百度词条)此算法是分治法和递归的完美使用,算法中使用了递归,不适合大数据量排序

public static void quickSort(int[] array,int begin,int end){
        if(end-begin<=1) {return ;}
        int x=array[begin];//31
        int low=begin;//0
        int high=end;//5
        //由于会在两头取数据,需要一个方向
        boolean direction =true;
        //开始进行数据的移动
        L1:
        while(low<high){
            if(direction){//从右往左找
                for(int i=high;i>low;i--){
                    if(array[i]<=x){
                        array[low++]=array[i];
                        high=i;
                        direction=!direction;
                        continue L1;
                    }
                }
                high=low;
            }else{
                for(int i=low;i<high;i++){
                    if(array[i]>=x){
                        array[high--]=array[i];
                        low=i;
                        direction=!direction;
                        continue L1;
                    }
                }
                low=high;
            }
        }
        //把最后找到的值放入中间位置
        array[low]=x;
        //开始完成左右两边的操作
        quickSort(array,begin,low-1);//L
        quickSort(array,low+1,end);//R
    }

3,基数排序

它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

举例子,对扑克牌的排序,要按花色和点数排序。那么基数排序就是先拿点数1-13分为13组,不同的点数放在不同的组,然后把这13组首位相连再

按花色排序,这样拍出来就是按点数和花色排好序的了(按不同的维度进行排序),适用于类别较明显的场景,如扑克牌,麻将等。

以麻将排序为例:

public static void radixSort(LinkedList<Mahjong> list){
        //先对点数进行分组
        LinkedList[] rankList=new LinkedList[9];
        for (int i=0;i<rankList.length;i++){
            rankList[i]=new LinkedList();
        }
        //把数据一个一个的放入到对应的组中
        while(list.size()>0){
            //取一个
            Mahjong m=list.remove();
            //放到组中
            rankList[m.rank-1].add(m);
        }
        //把9个组合到一起
        for (int i = 0; i < rankList.length; i++) {
            list.addAll(rankList[i]);
        }


        //先花色数进行分组
        LinkedList[] suitList=new LinkedList[3];
        for (int i=0;i<suitList.length;i++){
            suitList[i]=new LinkedList();
        }
        //把数据一个一个的放入到对应的组中
        while(list.size()>0){
            //取一个
            Mahjong m=list.remove();
            //放到组中
            suitList[m.suit-1].add(m);
        }
        //把3个组合到一起
        for (int i = 0; i < suitList.length; i++) {
            list.addAll(suitList[i]);
        }


    }

4,插入排序

输入一个元素,检查数组列表中的每个元素,将其插入到一个已经排好序的数列中的适当位置,使数列依然有序,当最后一个元素放入合适位置时,该数组排序完毕。其实质就是比较,交换位置。对于已经大部分有序的队列效率较高:

public void insertSort(int[] array){
        for(int i=1;i<array.length;i++){
            int j=i;
            int target=array[i];//表示想插入的数据
            while(j>0  && target<array[j-1]){//如果插入的数据小于数组的前一个时
                array[j]=array[j-1];
                j--;
            }
            array[j]=target;
        }
    }

5,希尔排序

希尔排序法(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法。是对插入排序的升级,解决插入排序

交换过多造成的性能消耗,适合大数据量排序(当然小数据两也没问题,步长  : (1,4,10,23,57,132,301,701,1750,…))

public static void shellSort(int[] array,int step){
        //3 9 1 2 5 4 7 8 6
        //2 5 1 3 8 4 7 9 6
        for(int k=0;k<step;k++) {//对步长的定位,选择每次操作的开始位置
            for(int i=k+step;i<array.length;i=i+step){//i表示从第2个数开始插入
                int j=i;
                int target=array[i];//表示想插入的数据
                while(j>step-1  && target<array[j-step]){//如果插入的数据小于数组的前一个时
                    array[j]=array[j-step];
                    j=j-step;
                }
                array[j]=target;
            }
        }
    }

6.堆排序

堆排序(号称大数据中使用最快的查找算法,哈哈没有做过深入研究) 一棵完全二叉树,如果某个节点的值总是不小于其父节点的值,则根节点的关键字是所有节点关键字中最小的,称为小根堆(小顶堆);如果某个节点的值总是不大于 其父节点的值,则根节点的关键字是所有节点关键字中最大的,称为大根堆(大顶堆)。 初始时把要排序的n个数看作是一棵顺序存储的完全二叉树,调整它们的存储顺序,使之 成为一个堆,将堆顶元素输出,得到n 个元素中最小(最大)的元素,这时堆的根节点的 数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得 到n 个元素中次小(或次大)的元素。依次类推,直到只有两个节点的堆,并对它们作交 换,最后得到有n个节点的有序序列。这个过程就称为堆排序。 参考:http://blog.csdn.net/u012152619/article/details/47452813
堆排序分为两步走,建堆和排序
public void sortHeap() {    int[] array = {12, 15, 25, 20, 14, 16, 50, 35, 30};    createHeap(array);    System.out.println("建堆完成:");    printArray(array);    int last = array.length - 1;    System.out.println("排序打印");    while (last >= 0) {        System.out.print(array[0] + " ");        swap(array, 0, last);        last--;        sortHeap(array, 0, last);    }}private void createHeap(int[] array) {    for (int i = (array.length - 1) / 2; i >= 0; i--) {        sortHeap(array, i, array.length);    }}/** * 调整数据以建成堆 * * @param array * @param sortIndex * @param lastIndex */private void sortHeap(int[] array, int sortIndex, int lastIndex) {    int leftChildIndex = 2 * sortIndex + 1;    int rightChildIndex = 2 * sortIndex + 2;    int smallerIndex = sortIndex;    if (leftChildIndex < lastIndex && array[leftChildIndex] < array[smallerIndex]) {        smallerIndex = leftChildIndex;    }    if (rightChildIndex < lastIndex && array[rightChildIndex] < array[smallerIndex]) {        smallerIndex = rightChildIndex;    }    if (smallerIndex != sortIndex) {        swap(array, sortIndex, smallerIndex);        sortHeap(array, smallerIndex, lastIndex);    }}private void swap(int[] array, int sortIndex, int smallerIndex) {    int temp = array[sortIndex];    array[sortIndex] = array[smallerIndex];    array[smallerIndex] = temp;}

7,图论----最小生成树 prim算法

MST(Minimum Spanning Tree,最小生成树)问题有两种通用的解法,Prim算法就是其中之一,它是从点的方面考虑构建一颗MST,大致思想是:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。(网上的定义)

由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点英语Vertex (graph theory),且其所有边的权值之和亦为最小。

所以最小生成树的作用就是找到连通图中连接各个顶点最小的路径。城市间网络建设,高速路修建等。

public void prim() {
    //当lowcost[i] == 0 时,表示节点i 已经被查找
    //如果lowcost[i] != 0 ,那么必然有一个值,
    //这个值可以用来表示查找到的集合(节点的连通分量)到
    //下标为i 的这个节点的距离
    int[] lowcost = new int[verticeSize];
        for(int i = 0; i < verticeSize; i++) {
        lowcost[i] = matrix[0][i];
        }
        //
        int min;
        int minId;
        int sum = 0;
        for(int i = 0; i < verticeSize; i++) {
        min = MAX_WEIGHT;
        minId = 0;
        //step1: 查找剩下的节点到已经被查找节点集合U中最短的边的节点
        for(int j = 1; j < verticeSize; j++) {
        if (lowcost[j] < min && lowcost[j] > 0) {
min = lowcost[j];
minId = j;
}
        }
       
        if (min == MAX_WEIGHT) {
break;
}
        sum +=min;
        //step2:找到这个节点后,我要更新剩下来的节点到已经被查找节点集合U 的距离
        lowcost[minId] = 0;
       
        for(int j = 1; j < verticeSize; j++) {
        if (lowcost[j] != 0 && matrix[minId][j] < lowcost[j]) {
lowcost[j] = matrix[minId][j];
}
        }
        }
        System.out.println("最小生成树代价:" + sum);
   
    }

8,图论--------Dijkstra算法

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。该算法用于最短路径查找

1)算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

2)算法步骤:

a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。

b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。

c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

d.重复步骤b和c直到所有顶点都包含在S中。

public Dijkstra(Graph g, int sourcePoint) {
graph = g;
sourceP= sourcePoint;
verticeS = new int[graph.verticeSize];
distance = new int[graph.verticeSize];
edge = graph.matrix;
calculate();
display();
}


private void display() {
for(int i = 0; i < graph.verticeSize; i++) {
System.out.println(i + ": " + distance[i]);
}

}


private void calculate() {

int minDis;
int u = 0;
// 初始化
for(int i = 0; i < graph.verticeSize; i++) {
distance[i] = edge[sourceP][i];
verticeS[i] = 0;
}
verticeS[sourceP] = 1; 

//轮询
for(int i = 1; i < graph.verticeSize; i++) {
minDis = graph.MAX_WEIGHT;
// 1.查找最短的那条边
// 将找到的边对应的节点保存到u
for(int j = 0; j < graph.verticeSize; j++) {
if (verticeS[j] == 0 && distance[j] < minDis) {
u = j;
minDis = distance[j];
}
}

//说明已经找完了
if (minDis == graph.MAX_WEIGHT) {
return;
}
verticeS[u] = 1;
//2. 更新distance,也就是起始点到其他未访问节点的距离
for(int j = 0; j < graph.verticeSize; j++) {
if (verticeS[j] == 0 && edge[u][j] < Graph.MAX_WEIGHT) {
if (distance[u] + edge[u][j] < distance[j]) {
distance[j] = distance[u] + edge[u][j];
}
}
}

}
}




原创粉丝点击