数据结构与算法-排序

排序(不全以后补)

基数排序

原理:
https://www.jb51.net/article/129428.htm

package com.atguigu.sort;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
 * @anthor shkstart
 * @create 2020-08-16 15:03
 */
public class JSsort {
	@Test
	    public void test(){
		int[] array = {135,242,192,93,345,11,24,19};
		radixSort(array);
	}
	public void radixSort(int[] array){
		int max = array[0];
		for (int i=0;i<array.length;i++){
			//找到数组中的最大值
			if(array[i]>max){
				max = array[i];
			}
		}
		int keysNum = 0;
		//关键字的个数,我们使用个位、十位、百位...当做关键字,所以关键字的个数就是最大值的位数
		while(max>0){
			max /= 10;
			keysNum++;
		}
		List<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();
		for (int i=0;i<10;i++){
			//每位可能的数字为0~9,所以设置10个桶
			buckets.add(new ArrayList<Integer>());
			//桶由ArrayList<Integer>构成
		}
		for (int i=0;i<keysNum;i++){
			//由最次关键字开始,依次按照关键字进行分配
			for (int j=0;j<array.length;j++){
				//扫描所有数组元素,将元素分配到对应的桶中
				//取出该元素对应第i+1位上的数字,比如258,现在要取出十位上的数字,258%100=58,58/10=5
				int key =array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
				buckets.get(key).add(array[j]);
				//将该元素放入关键字为key的桶中
			}
			//分配完之后,将桶中的元素依次复制回数组
			int counter = 0;
			//元素计数器
			for (int j=0;j<10;j++){
				ArrayList<Integer> bucket =buckets.get(j);
				//关键字为j的桶
				while(bucket.size()>0){
					array[counter++] = bucket.remove(0);
					//将桶中的第一个元素复制到数组,并移除
				}
			}
			System.out.print("第"+(i+1)+"轮排序:");
			for (int n= 0;n < array.length;n++){
				System.out.print(array[n]+ "   ");
			}
			System.out.println();
		}
	}
}

快速排序

原理




不妨取首元素m = S[lo]作为候选,将其从向量中取出并做备份,腾出的空闲单元便于其它元素的位置调整。然后如图(b)所示,不断试图移动lo和hi,使之相互靠拢。当然,整个移动过程中,需始终保证lo(hi)左侧(右侧)的元素均不大于(不小于)m。最后如图(c)所示,当lo与hi彼此重合时,只需将原备份的m回填至这一位置,则S[lo = hi]=m便成为一个名副其实的轴点

package com.atguigu.sort;
import org.junit.Test;
/**
 * @anthor shkstart
 * @create 2020-08-16 15:03
 */
public class Quicksort {
	@Test
	    public void test() {
		int[] array = {135,242,192,93,345,11,24,19};
		this._elem = array;
		this.quickSort(0,8);
		for (int i = 0;i < 8;i++){
			System.out.print(this._elem[i] + "   ");
		}
	}
	public int[] _elem = new int[8];
	public void quickSort(int lo,int hi){
		if (hi - lo < 2) return;
		int mi = partition1(lo,hi - 1);
		quickSort(lo,mi);
		quickSort(mi+1,hi);
	}
	/**
     * 子任务规模接近在这里却无法保证
     * 若在最终有序向量中该候选元素的秩为r,则子向量的规模必为r和n - r - 1。
     * 特别地,r = 0时子向量规模分别为0和n - 1左侧子向量为空,而右侧子向量与原向量几乎等长
     */
	public int partition1(int lo,int hi){
		swap(_elem[lo],_elem[(int) (lo + Math.random()*(100)%(hi - lo + 1))]);
		int pivot = _elem[lo];
		while (lo < hi){
			while ((lo < hi) && (pivot <= _elem[hi])){
				hi--;
			}
			_elem[lo] = _elem[hi];
			while ((lo < hi) && (_elem[lo] <= pivot)){
				lo++;
			}
			_elem[hi] = _elem[lo];
		}
		_elem[lo] = pivot;
		return lo;
	}
	public void swap(int a,int b){
		int c = a;
		a = b;
		b = c;
	}
	/**
     * 将交替地将右(左)侧元素转移至左(右)侧,并最终恰好将轴点置于正中央的位置。
     * 这就意味着,退化的输入向量能够始终被均衡的切分,如此反而转为最好情况,
     */
	public int partition2(int lo,int hi){
		swap(_elem[lo],_elem[(int) (lo + Math.random()*(100)%(hi - lo + 1))]);
		int pivot = _elem[lo];
		while (lo < hi){
			while ((lo < hi)){
				if (pivot < _elem[hi]){
					hi--;
				} else {
					_elem[lo++] = _elem[hi];
					break;
				}
			}
			while ((lo < hi)){
				if (_elem[lo] < pivot){
					lo++;
				} else {
					_elem[hi--] = _elem[lo];
					break;
				}
			}
		}
		_elem[lo] = pivot;
		return lo;
	}
}

实现


/**
     * 子任务规模接近在这里却无法保证
     * 若在最终有序向量中该候选元素的秩为r,则子向量的规模必为r和n - r - 1。
     * 特别地,r = 0时子向量规模分别为0和n - 1左侧子向量为空,而右侧子向量与原向量几乎等长
     */
public int partition1(int lo,int hi){
	swap(_elem[lo],_elem[(int) (lo + Math.random()*(100)%(hi - lo + 1))]);
	int pivot = _elem[lo];
	while (lo < hi){
		while ((lo < hi) && (pivot <= _elem[hi])){
			hi--;
		}
		_elem[lo] = _elem[hi];
		while ((lo < hi) && (_elem[lo] <= pivot)){
			lo++;
		}
		_elem[hi] = _elem[lo];
	}
	_elem[lo] = pivot;
	return lo;
}
public void swap(int a,int b){
	int c = a;
	a = b;
	b = c;
}

实例

改进


考查所有(或几乎所有)元素均重复的退化情况。partition()算法的版本A对此类输入的处理完全等效于此前所举的最坏情况。事实上对于此类向量,主循环内部前一子循环的条件中“pivot <= elem[hi]”形同虚设,故该子循环将持续执行,直至“lo < hi”不再满足

/**
     * 将交替地将右(左)侧元素转移至左(右)侧,并最终恰好将轴点置于正中央的位置。
     * 这就意味着,退化的输入向量能够始终被均衡的切分,如此反而转为最好情况,
     */
public int partition2(int lo,int hi){
	swap(_elem[lo],_elem[(int) (lo + Math.random()*(100)%(hi - lo + 1))]);
	int pivot = _elem[lo];
	while (lo < hi){
		while ((lo < hi)){
			if (pivot < _elem[hi]){
				hi--;
			} else {
				_elem[lo++] = _elem[hi];
				break;
			}
		}
		while ((lo < hi)){
			if (_elem[lo] < pivot){
				lo++;
			} else {
				_elem[hi--] = _elem[lo];
				break;
			}
		}
	}
	_elem[lo] = pivot;
	return lo;
}

选取与中位数

众数

  • 原理



  • 实现
package com.atguigu.sort;
import org.junit.Test;
/**
 * @anthor shkstart
 * @create 2020-08-16 20:39
 */
public class allFind {
	@Test
	    public void test() {
		int[] array = {135,242,192,93,345,11,24,19,19,19,19,19};
		majority(array,0);
	}
	public int[] _elem = new int[8];
	public Boolean majority(int[] A,int maj){
		maj = majEleCandidate(A);
		System.out.println(maj);
		return majEleCheck(A,maj);
	}
	public Boolean majEleCheck(int[] A,int maj){
		int occurrence = 0;
		for (int i = 0;i < A.length;i++){
			if (A[i] == maj) occurrence++;
		}
		return (2*occurrence > A.length);
	}
	public int majEleCandidate(int[] A){
		int maj = 0;
		for (int c = 0,i = 0; i < A.length;i++){
			if (0 == c){
				maj = A[i];
				c = 1;
			} else {
				if (maj == A[i]) {
					c++;
				} else {
					c--;
				}
			}
		}
		return maj;
	}
}

中位数

蛮力算法

/**
     * 蛮力算法
     * @param s1
     * @param lo1
     * @param n1
     * @param s2
     * @param lo2
     * @param n2
     * @return
     */
public int trivialMedian(int[] s1,int lo1,int n1,int[] s2,int lo2,int n2){
	int hi1 = lo1 + n1,hi2 = lo2 + n2;
	Vector s = new Vector();
	while ((lo1 < hi1) && (lo2 < hi2)){
		while ((lo1 < hi1) && (s1[lo1] <= s2[lo2])) s.add(s1[lo1++]);
		if (lo1 == 8) break;
		//以免出现越界,可能换成向量不用写这个
		while ((lo2 < hi2) && (s2[lo2] <= s1[lo1])) s.add(s2[lo2++]);
	}
	while (lo1 < hi1) s.add(s1[lo1++]);
	while (lo2 < hi2) s.add(s2[lo2++]);
	return (int) s.elementAt((n1 + n2 +1)/2);
}

两个都为n


/**
     * 子串的长度相同
     * @param s1
     * @param lo1
     * @param s2
     * @param lo2
     * @param n
     * @return
     */
public int median1(int[] s1,int lo1,int[] s2,int lo2,int n){
	if (n < 3) return trivialMedian(s1,lo1,n,s2,lo2,n);
	int mi1 = lo1 + n / 2,mi2 = lo2 + (n - 1)/2;
	if (s1[mi1] < s2[mi2]){
		return median1(s1,mi1,s2,lo2,n + lo1 - mi1);
	} else if (s1[mi1] > s2[mi2]){
		return median1(s1,lo1,s2,mi2,n + lo2 -mi2);
	} else {
		return s1[mi1];
	}
}

一般情况


public int median2(int[] s1,int lo1,int n1,int[] s2,int lo2,int n2){
	if (n1 > n2) return median2(s2,lo2,n2,s1,lo1,n1);
	//因为下面仅针对n1 <= n2的情况在讨论
	if (n2 < 6) //蛮力算法
	return trivialMedian(s1,lo1,n1,s2,lo2,n2);
	if(2 * n1 < n2){
		//若两个向量的长度相差悬殊,则长者(S2)的两翼可直接截除
		return median2(s1,lo1,n1,s2,lo2 + (n2 - n1 -1)/2,n1 + 2 - (n2 -n1)%2);
		//这里怎么取两边的可以看一下
	}
	int mi1 = lo1 + n1/2;
	int mi2a = lo2 + (n1 - 1)/2;
	//这里其实是保证截取的左右平衡
	int mi2b = lo2 + n2 -1 - n1/2;
	if (s1[mi1] > s2[mi2b]){
		return median2(s1,lo1,n1/2 + 1,s2,mi2a,n2 - (n1 - 1)/2);
	} else if (s1[mi1] < s2[mi2a]){
		return median2(s1,mi1,(n1+1)/2,s2,lo2,n2 - n1/2);
	} else {
		return median2(s1,lo1,n1,s2,mi2a,n2 - (n1 - 1)/2 *2);
	}
}

总的

package com.atguigu.sort;
import org.junit.Test;
import java.util.Vector;
/**
 * @anthor shkstart
 * @create 2020-08-16 21:07
 */
public class middleFind {
	@Test
	    public void test(){
		int[] array1 = {1,2,3,4,5,6,7,8};
		int[] array2 = {9,10,11,12,13,14,15,16};
		System.out.println(trivialMedian(array1,0,8,array2,0,8));
	}
	/**
     * 蛮力算法
     * @param s1
     * @param lo1
     * @param n1
     * @param s2
     * @param lo2
     * @param n2
     * @return
     */
	public int trivialMedian(int[] s1,int lo1,int n1,int[] s2,int lo2,int n2){
		int hi1 = lo1 + n1,hi2 = lo2 + n2;
		Vector s = new Vector();
		while ((lo1 < hi1) && (lo2 < hi2)){
			while ((lo1 < hi1) && (s1[lo1] <= s2[lo2])) s.add(s1[lo1++]);
			if (lo1 == 8) break;
			//以免出现越界,可能换成向量不用写这个
			while ((lo2 < hi2) && (s2[lo2] <= s1[lo1])) s.add(s2[lo2++]);
		}
		while (lo1 < hi1) s.add(s1[lo1++]);
		while (lo2 < hi2) s.add(s2[lo2++]);
		return (int) s.elementAt((n1 + n2 +1)/2);
	}
	/**
     * 子串的长度相同
     * @param s1
     * @param lo1
     * @param s2
     * @param lo2
     * @param n
     * @return
     */
	public int median1(int[] s1,int lo1,int[] s2,int lo2,int n){
		if (n < 3) return trivialMedian(s1,lo1,n,s2,lo2,n);
		int mi1 = lo1 + n / 2,mi2 = lo2 + (n - 1)/2;
		if (s1[mi1] < s2[mi2]){
			return median1(s1,mi1,s2,lo2,n + lo1 - mi1);
		} else if (s1[mi1] > s2[mi2]){
			return median1(s1,lo1,s2,mi2,n + lo2 -mi2);
		} else {
			return s1[mi1];
		}
	}
	public int median2(int[] s1,int lo1,int n1,int[] s2,int lo2,int n2){
		if (n1 > n2) return median2(s2,lo2,n2,s1,lo1,n1);
		//因为下面仅针对n1 <= n2的情况在讨论
		if (n2 < 6) //蛮力算法
		return trivialMedian(s1,lo1,n1,s2,lo2,n2);
		if(2 * n1 < n2){
			//若两个向量的长度相差悬殊,则长者(S2)的两翼可直接截除
			return median2(s1,lo1,n1,s2,lo2 + (n2 - n1 -1)/2,n1 + 2 - (n2 -n1)%2);
			//这里怎么取两边的可以看一下
		}
		int mi1 = lo1 + n1/2;
		int mi2a = lo2 + (n1 - 1)/2;
		//这里其实是保证截取的左右平衡
		int mi2b = lo2 + n2 -1 - n1/2;
		if (s1[mi1] > s2[mi2b]){
			return median2(s1,lo1,n1/2 + 1,s2,mi2a,n2 - (n1 - 1)/2);
		} else if (s1[mi1] < s2[mi2a]){
			return median2(s1,mi1,(n1+1)/2,s2,lo2,n2 - n1/2);
		} else {
			return median2(s1,lo1,n1,s2,mi2a,n2 - (n1 - 1)/2 *2);
		}
	}
}

基于优先级队列


第一种算法如图(a1)所示。首先,花费O(n)时间将全体元素组织为一个小顶堆;然后,经
过k次delMin()操作,则如图(a2)所示得到位序为k的元素

@Test
    public void test1() {
	Integer[] array = {135,242,192,93,345,11,24,19,19,19,19,19};
	PQ_CompHeap pq = new PQ_CompHeap(array,4);
	for (int i = 0;i < array.length;i++){
		pq.insert(array[array.length - 4 -1 + i]);
		pq.delMax();
	}
	System.out.println(pq.getMax());
}


任取k个元素,并在O(k)时间以内将其组织为大顶堆。然后将剩余的n - k个元素逐个插入堆中;每插入一个,随即删除堆顶,以使堆的规模恢复为k。待所有元素处理完毕之后,堆顶即为目标元素

@Test
    public void test2() {
	Integer[] array = {135,242,192,93,345,11,24,19,19,19,19,19};
	PQ_CompHeap pq = new PQ_CompHeap(array,array.length);
	for (int i = 0;i < 6;i++){
		pq.delMin();
	}
	System.out.println(pq.getMin());
}


首先将全体元素分为两组,分别构建一个规模为n - k的小顶堆G和一个规模为k的大顶堆H。接下来,反复比较它们的堆顶g和h,只要g < h,则将二者交换,并重新调整两个堆。如此,G的堆顶g将持续增大,H的堆顶h将持续减小。当g>=h时,h即为所要找的元素

@Test
    public void test3() {
	Integer[] array1 = {135,242,192,93,345,11,24,19,19,19,19,19};
	Integer[] array2 = new Integer[array1.length - 4];
	for (int i = 4;i < array1.length;i++){
		array2[i-4] = array1[i];
	}
	PQ_CompHeap pq1 = new PQ_CompHeap(array2,array1.length - 4);
	PQ_CompHeap pq2 = new PQ_CompHeap(array1,4);
	while (pq1.getMin() < pq2.getMax()){
		swap(pq1.getMin(),pq2.getMax());
	}
	System.out.println(pq2.getMax());
}
public void swap(Integer a,Integer b){
	Integer c = a;
	a = b;
	b = c;
}

基于快速划分


调用算法partition()构造向量A的一个轴点A[i] = x。若i =k,则该轴点恰好就是待选取的目标元素,即可直接将其返回;没有的话,相应减去不可能包含的部分如L段、G段

K-选取算法


package com.atguigu.sort;
import org.junit.Test;
import java.util.Vector;
/**
 * @anthor shkstart
 * @create 2020-08-18 8:34
 */
public class K_Find {
	@Test
	    public void test(){
		int[] array = {135,242,192,93,345,11,24,19};
	}
	public static int Q = 5;
	public int select(Vector A, int k){
		if (A.size() < Q){
			return trivialSelect(A,k);
		}
		Vector B = new Vector();
		int[] C = new int[Q];
		for (int i = 1;i <= A.size()/Q;i++){
			for (int j = (i-1) * Q;j < Q*i;j++){
				C[j - (i-1) * Q] =(int) A.elementAt((i-1) * Q);
			}
			sort(C);
			B.add(meddile(C));
		}
		int m = select(B,C.length/2);
		int i = 0;
		Vector l = new Vector();
		int e = 0;
		Vector g = new Vector();
		while (i < A.size()){
			if ((int)A.elementAt(i) < m){
				l.add((int)A.elementAt(i));
			} else if((int)A.elementAt(i) == m){
				e++;
			} else {
				g.add((int)A.elementAt(i));
			}
			i++;
		}
		if (l.size() >= k) {
			return select(l,k);
		} else if (l.size() + e >= k){
			return m;
		} else {
			return select(g,k - l.size() - e);
		}
	}
	public int trivialSelect(Vector A,int k){
		return 0;
	}
	public void sort(int[] A){
	}
	public int meddile(int[] A){
		return 0;
	}
}

希尔排序

原理



实现(感觉很难按上面的方式写,先复制了一个,以后改)

package sortdemo;
import java.util.Arrays;
/**
 * Created by chengxiao on 2016/11/24.
 */
public class ShellSort {
	public static void main(String []args){
		int []arr ={1,4,2,7,9,8,3,6};
		sort(arr);
		System.out.println(Arrays.toString(arr));
		int []arr1 ={1,4,2,7,9,8,3,6};
		sort1(arr1);
		System.out.println(Arrays.toString(arr1));
	}
	/**
     * 希尔排序 针对有序序列在插入时采用交换法
     * @param arr
     */
	public static void sort(int []arr){
		//增量gap,并逐步缩小增量
		for (int gap=arr.length/2;gap>0;gap/=2){
			//从第gap个元素,逐个对其所在组进行直接插入排序操作
			for (int i=gap;i<arr.length;i++){
				int j = i;
				while(j-gap>=0 && arr[j]<arr[j-gap]){
					//插入排序采用交换法
					swap(arr,j,j-gap);
					j-=gap;
				}
			}
		}
	}
	/**
     * 希尔排序 针对有序序列在插入时采用移动法。
     * @param arr
     */
	public static void sort1(int []arr){
		//增量gap,并逐步缩小增量
		for (int gap=arr.length/2;gap>0;gap/=2){
			//从第gap个元素,逐个对其所在组进行直接插入排序操作
			for (int i=gap;i<arr.length;i++){
				int j = i;
				int temp = arr[j];
				if(arr[j]<arr[j-gap]){
					while(j-gap>=0 && temp<arr[j-gap]){
						//移动法
						arr[j] = arr[j-gap];
						j-=gap;
					}
					arr[j] = temp;
				}
			}
		}
	}
	/**
     * 交换数组元素
     * @param arr
     * @param a
     * @param b
     */
	public static void swap(int []arr,int a,int b){
		arr[a] = arr[a]+arr[b];
		arr[b] = arr[a]-arr[b];
		arr[a] = arr[a]-arr[b];
	}
}

改进



原文地址:https://www.cnblogs.com/suit000001/p/13526456.html