几种排序小结 JAVA语言描述

插入排序

针对一个已经排好序的子线性表,不断的插入元素进去,直到所有的元素都是排好序的。

对于一个有序的线性表list[ ],元素a=list[i]插入会依次与线性表中的元素进行比较。如果list[n]>a,则将list[n]后移一位至list[n+1](此时list[n]=list[n+1]);如果list[k]<a ,此时list[k+1] = list[k+2]>a 则a插入list[k+1]。

public static void insertionSort(int[] list) {
		for (int i = 1; i < list.length; i++) {
			int temp = list[i]; //从数组的第2个元素开始
			int k;
			for (k = i - 1; k >= 0; k--) { //temp为数组的第2个元素,且在本for循环内只负责与i之前的数组元素进行比较
				if (temp < list[k]) {
					list[k + 1] = list[k];//如果元素大于temp,
					//则把当前的元素后移,直到第一个元素或者当前元素大于temp
				} else
					break;
			}
			list[k + 1] = temp;//把元素插入第k个元素位置上,此时有2种情况
			//1、所有元素都大于temp,此时k=-1,k+1 =0。也就是说temp是最小的元素,此时应该在第一个
			//2、下标为[k]的元素小于temp,此时list[k+1]=list[k+2]>temp,所以temp应该插入list[k+1]
		}
	}

时间复杂度:

对于一个有着n个元素的线性表来说,第一次迭代需要比较2个元素的值并且还需要检测下标(1次比较1次移动=2×1),第二次迭代需要比较3个元素的值和下标(2次比较2次移动2×2)。设c为每次迭代的赋值等其他操作。那么时间的复杂度就是

T(n)=2×1+2×2+2×3+2×4+···+2×(n-1)+c×(n-1)


冒泡排序

冒泡排序会多次遍历数组,在每次遍历中连续比较相邻的元素,如果元素没有按照顺序排列,则互换它们的值。

由于较大的数字不断的与相邻的数字比较并且向数组的末尾移动,而较小的数字不断的向数组的头部移动,这看上去类似于“冒泡”,所以称之为“冒泡排序”。

下面是我自己写的冒泡排序的代码:

public static void BubbleSort(int[] list) {
		for (int i = 0, k = 1; (i < list.length - k)
				&& (k <= list.length - 1); i++) {
			
			int temp = list[i];
			if (list[i] > list[i + 1]) {
				list[i] = list[i + 1];
				list[i + 1] = temp;
			}
			if (i == list.length - k - 1) {
				k++;
				i = -1;
			} // 每次循环到末尾的时候,末尾已经是线性表的最大数,因此k+1,下次循环就不需要再比较这个末尾的数字
		}//每次循环都会把最大的数字排在数列的末尾,每次循环以后k+1,下次循环的比较就会少1次,因为最大的数字在末尾是不需要比较的
	}

下面是书籍中的代码:

public static void BubbleSort1(int[] list) {
		boolean needNextPass = true;
		for (int k = 1; k < list.length && needNextPass; k++) {
			needNextPass = false;
			for (int i = 0; i < list.length - k; i++) {
				if (list[i] > list[i + 1]) {
					int temp = list[i];
					list[i] = list[i + 1];
					list[i + 1] = temp;
					needNextPass = true;
				}
			}
		}
	}

为了区别这2个代码的性能优劣,我随机了1w个整型的数据进行比较。使用BubbleSort的时间是140毫秒BubbleSort1的时间是107毫秒。使用10w个整型的结果是,使用BubbleSort是17835毫秒,而使用BubbleSort1是12027毫秒。分析可知:第二段代码中的needNextPass是提高性能的关键,在已经排好序的情况下(程序没有完全遍历数组就已经是排好序的情况)程序将不会再进行完整的遍历。因此节省了时间。

时间复杂度:


归并排序

归并排序将数组分为两半,对每部分递归的应用归并排序。在两部分都排好序后,对它们进行归并。

如果 list.length > 0,则会报错。原因是如果list.length = 1,那么会导致有个数组的初始长度被设置为0.

public static void mergeSort(int[] list) {
		if (list.length > 1) { //这里必须是1,如果是0的话会报java.lang.StackOverflowError错误
			int[] firstHalf = new int[list.length / 2];
			System.arraycopy(list, 0, firstHalf, 0, list.length / 2);
			mergeSort(firstHalf);

			int[] secondHalf = new int[list.length - list.length / 2];
			System.arraycopy(list, list.length / 2, secondHalf, 0,
					secondHalf.length);
			mergeSort(secondHalf);

			merge(firstHalf, secondHalf, list);
		}
	}

	public static void merge(int[] list1, int[] list2, int[] temp) {
		int current1 = 0; // 这3个int用于下标
		int current2 = 0;
		int current3 = 0;
		while (current1 < list1.length && current2 < list2.length) {
			if (list1[current1] < list2[current2]) {
				//list1当前数小,则加入temp,并且temp和list1的下标增加
				temp[current3++] = list1[current1++];
			} else {//list2当前数小,则加入temp,并且temp和list2的下标增加
				temp[current3++] = list2[current2++];
			}
		}//如果循环跳出后,list1或者list2还未完结,那么就把剩下的元素直接插入temp
		while(current1<list1.length) { temp[current3++] = list1[current1++]; }
		while(current2<list2.length) { temp[current3++] = list2[current2++]; }
	}

时间复杂度:


快速排序

在数组中选择一个称为主元(pivot)的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于主元,而第二部分中的所有元素都大于主元。对第一部分递归的用快速排序算法,然后对第二部分递归的应用快速排序算法。

首先是默认选择首个元素为主元(pivot)的排序代码:

private static void quickSort(int[] list, int first, int last) {
    if (last > first) {
      int pivotIndex = partition(list, first, last);
      quickSort(list, first, pivotIndex - 1);
      quickSort(list, pivotIndex + 1, last);
    }
  }

  /** Partition the array list[first..last] */
  private static int partition(int[] list, int first, int last) {
    int pivot = list[first]; // Choose the first element as the pivot
    int low = first + 1; // Index for forward search
    int high = last; // Index for backward search

    while (high > low) {
      // Search forward from left
      while (low <= high && list[low] <= pivot)
        low++;

      // Search backward from right
      while (low <= high && list[high] > pivot)
        high--;

      // Swap two elements in the list
      if (high > low) {
        int temp = list[high];
        list[high] = list[low];
        list[low] = temp;
      }
    }

    while (high > first && list[high] >= pivot)
      high--;

    // Swap pivot with list[high]
    if (pivot > list[high]) {
      list[first] = list[high];
      list[high] = pivot;
      return high;
    }
    else {
      return first;
    }
  }

这种方法在倒序的数组中复杂度最高,因为每次都会分成一个包含list.length-1个元素的数组和一个0个元素的数组,剩下来就是这个主元。

时间复杂度:

平均情况下的时间复杂度为 T(n) = O(nlogn)

最差情况下的时间复杂度为 T(n) = O(n*n)

使用“三项数据取中”的办法处理:

通过选取特定的中间值的点来进行pivot的设置,从而避免倒序取首位的情况。

public static void recQuickSort(int left, int right) {
		if (right - left + 1 <= 3) {
			manualSort(left, right);
		} else {
			//找出首、尾和中点 3者中的中间值pivot,将pivot放入right-1位置
			int median = medianof3(left, right);
			//根据pivot找出划分list的下标的值partition
			int partition = partitionIt(left, right, median);
			//根据partition来继续将list划分成左右两块
			recQuickSort(left, partition - 1);
			recQuickSort(partition + 1, right);
		}
	}

	private static int partitionIt(int left, int right, int median) {
		// TODO Auto-generated method stub
		int leftPtr = left;
		int rightPtr = right - 1;
		while (true) {
			while (list[++leftPtr] < median);
			while (list[--rightPtr] > median);

			if (leftPtr < rightPtr)
				swap(leftPtr, rightPtr);
			else break;
		}
		swap(leftPtr,right-1);
		return leftPtr;
	}

	private static int medianof3(int left, int right) {
		// TODO Auto-generated method stub
		int center = (left + right) / 2;
		if (list[left] > list[center])
			swap(left, center);
		if (list[left] > list[right])
			swap(left, right);
		if (list[center] > list[right])
			swap(center, right);

		swap(center, right - 1);

		return list[right - 1];
	}

	public static void manualSort(int left, int right) {
		if(right-left+1 ==1) return;
		else if(right-left+1==2) {
			if(list[left]>list[right]) swap(left,right);
		} else {
			int center = (left + right) / 2;
			if (list[left] > list[center])
				swap(left, center);
			if (list[left] > list[right])
				swap(left, right);
			if (list[center] > list[right])
				swap(center, right);
		}
	}
	public static void swap(int left, int right) {
		int temp = list[left];
		list[left] = list[right];
		list[right] = temp;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值