数据结构知识整理(三)——排序

1、冒泡排序

冒泡排序是排序算法中最基本的一种排序方法,该方法逐次比较两个相邻数据的大小并交换位置来完成对数据排序,每次比较的结果都找出了这次比较中数据的最大项,因为是逐次比较,所以效率是O(N^2)的。

public void bubbleSort() {
		int out,in;
		for(out=index-1; out>1; --out) {
			for(in=0; in<out; ++in) {
				if(array[in]>array[in+1]) {
					swap(in, in+1);
				}
			}
		}
	}
	
	public void swap(int dex1, int dex2) {
		int temp = array[dex1];
		array[dex1] = array[dex2];
		array[dex2] = temp;
	}

2、选择排序

选择排序对冒泡排序进行了优化,在每次遍历比较的过程中不进行交换,而是记录本次遍历的最小值,在遍历结束后再将最小值移到这次遍历的开始位置。这样虽然比较次数没有改变,但交换的次数大大减少,一共只需要N次交换。因为比较的次数没变,所以效率任然是O(N^2)的。

public void selectionSort() {
		int out, in;
		for(out=0; out<index-1; ++out) {
			int temp = out;
			for(in=out+1; in<index; ++in) {
				if(array[in] < array[temp]) {
					temp = in;
				}
			}
			swap(out, temp);
		}
	}

3、插入排序

插入排序充分利用已排列好的数据,然后将未排序的数据插入到已排数据的队伍当中,这样每插入一个未排序数据已排队伍都将增加一个成员,最终达到排序的目的。

public void insertionSort() {
		int out ,in;
		for(out=1; out<index; ++out) {
			int temp = array[out];
			in = out;
			while(in>0 && temp<array[in-1]) {
				array[in] = array[in-1];
				--in;
			}
			array[in] = temp;
		}
	}

4、归并排序

归并排序是将两个有序数组合并为一个有序数组的排序,应用在一般排序上要结合二分法递归地将数组依次归并,最终得到一个大的有序数组。归并的效率是O(NlogN)的,但要额外开辟一个数组来存放临时数据,所以占用空间要大一倍。

public void mergeSort() {
		int[] newArray = new int[index];
		recMergeSort(newArray, 0, index-1);
		
	}
	
	private void recMergeSort(int[] data, int low, int upper) {
		if(low == upper) {
			return;
		}
		int mid = (low + upper)/2;
		recMergeSort(data, mid+1, upper);
		recMergeSort(data, low, mid);
		merge(data, low, mid+1, upper);
	}
	
	private void merge(int[] data, int lowStart, int highStart, int upperBound) {
		int j = 0;
		int lowBound = lowStart;
		int mid = highStart - 1;
		int num = upperBound - lowStart + 1;
		
		while(lowStart<=mid && highStart<=upperBound) {
			if(array[lowStart] < array[highStart]) {
				data[j++] = array[lowStart++];
			} else {
				data[j++] = array[highStart++];
			}
		}
		
		while(lowStart<=mid) {
			data[j++] = array[lowStart++];
		}
		
		while(highStart<=upperBound) {
			data[j++] = array[highStart++];
		}
		
		for(j=0; j<num; j++) {
			array[lowBound+j] = data[j];
		}
	}


5、希尔排序

希尔排序是一种高级排序,它是由插入排序进化来的,插入排序是将未排的数据依次与前面已排好的数据进行比较移动,这样如果一个较小的数排在靠后的位置,那么要找到这个数的正确位置就要进行较多次移动。希尔排序改进了这种方式,它将每次比较的间隔扩大,排过一次之后数据就分阶段有序了,之后逐渐缩小这个间隔再进行排序。这样做的目的就是让数据一开始可以在一个较大的范围内进行移动,待基本有序后数据的移动量就小了很多。

public void shellSort() {
		int in, out;
		int h = 1;
		int temp;
		while(h < index/3) {
			h = h*3+1;
		}
		while(h>0) {
			for(out=h; out<index; ++out) {
				temp = array[out];
				in = out;
				while(in>h-1 && array[in-h] > temp) {
					array[in] = array[in-h];
					in -=h;
				}
				array[in] = temp;
			}
			h = (h-1)/3;
		}
	}

希尔排序中关键是对数据间隔h的选择,一个间隔序列是由Knuth提出的,即h=h*3+1,h的初始值为1,这是希尔排序中最优的间隔序列。

6、快速排序

快速排序是一种广泛使用的排序方法,效率可以达到O(NlogN),快速排序的原理是确定一个中间值pivot,将所有小于pivot的数据放在左侧,大于pivot的值放在右侧,之后再对左右两侧分别采取这种策略进行排序,直到这个过程结束。

private int partition(int left, int right, int pivot) {
		int leftPtr = left;
		int rightPtr = right-1;
		while(true) {
			while(array[++leftPtr] < pivot) ;
			while(array[--rightPtr] > pivot);
			if(leftPtr >= rightPtr) {
				break;
			} else {
				swap(leftPtr, rightPtr);
			}
		}
		swap(leftPtr, right-1);
		return leftPtr;
	}
	
	private int median(int left, int right) {
		int center = (left+right)/2;
		if(array[left]>array[center]) {
			swap(left, center);
		}
		if(array[left]>array[right]) {
			swap(left, right);
		}
		if(array[center]>array[right]) {
			swap(center, right);
		}
		swap(center, right-1);
		return array[right-1];
	}
	
	private void manulSort(int left, int right) {
		int size = right-left+1;
		if(size <= 1) return;
		if(size == 2) {
			if(array[left]>array[right]) swap(left, right);
		} else {
			if(array[left]>array[right-1]) swap(left, right-1);
			if(array[left]>array[right]) swap(left, right);
			if(array[right-1]>array[right]) swap(right-1, right);
		}
	}
	
	private void recQuickSort(int left, int right) {
		int size = right-left+1;
		if(size<=3) {
			manulSort(left, right);
		} else {
			int pivot = median(left, right);
			int partition = partition(left, right, pivot);
			recQuickSort(left, partition-1);
			recQuickSort(partition+1, right);
		}
		
	}
	
	public void quickSort() {
		recQuickSort(0, index-1);
	}

快速排序的关键是确定中间值pivot,如果中间值选取的不好,会使快速排序的效率降到O(N^2)。上面的例子采用了三选一的策略来确定中间值,即在要排序的数据中选择左端、中间和右端三个数据后比较取中间值;还有在数据量较小时,比如小于三个则直接手动排序。

快速排序选中间值实际上采用的是分治的思想,对数据的准确划分才能达到最高的效率,更深层的原理可以去看这篇文章

数学之美番外篇:快排为什么那样快——刘未鹏



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值