归并排序实现

         归并排序被列为基础排序算法的第二位,仅次于快排。主要思想是将数组分成两两一组,排序各个分组,然后再将已经排序好的分组看成一个元素,再两两分组并排序。

         java代码:

/**
	 * @Title: mergeSort
	 * @Description: TODO(两路归并算法)
	 * @param @param is
	 * @param @param left
	 * @param @param right 设定文件
	 * @return void 返回类型
	 */
	private void mergeSort(int[] is, int left, int right) {
		if (left < right) {
			int middle = (left + right) / 2;

			// 对左边进行递归
			mergeSort(is, left, middle);
			// 对you边进行递归
			mergeSort(is, middle + 1, right);
			// 合并
			merge(is, left, middle, right);
		}
	}

 合并算法:

private void merge(int[] is, int left, int middle, int right) {
		// 右边的起始位置
		int mid = middle + 1;
		int tem = left;
		int temp = left;
		// 循环遍历,从两个数组中找出较小的放入中间数组
		while (left <= middle && mid <= right) {
			if (is[left] <= is[mid]) {
				tempArr[temp++] = is[left++];
			} else {
				tempArr[temp++] = is[mid++];
			}
		}
		// 将剩余数组里面的数放入临时数组
		while (left <= middle) {
			tempArr[temp++] = is[left++];
		}
		while (mid <= right) {
			tempArr[temp++] = is[mid++];
		}
		// 将临时数组拷贝回原数组
		while (tem <= right) {
			is[tem] = tempArr[tem++];
		}
		// System.arraycopy(tempArr, tem, is, tem, (right - tem + 1));

	}

      排序1000000个乱序数组,归并排序耗时176毫秒,比快排序慢,优于希尔排序。

      做完两路归并排序,做一下三路归并排序,代码如下:

/**
	 * @Title: mergeSort3
	 * @Description: TODO(三路归并)
	 * @param @param is
	 * @param @param left
	 * @param @param right 设定文件
	 * @return void 返回类型
	 */
	private void mergeSort3(int[] is, int left, int right) {
		
		if (left < right) {
			if(left < right - 2){
				int len = (right + 1 - left) / 3;
				int leftMid = left + len;
				int rightMid = left + len + len;
//				System.out.println(left+"\t"+right+"\t"+leftMid+"\t"+rightMid);
				// 对第一段进行递归
				mergeSort3(is, left, leftMid);
				// 对第二段进行递归
				mergeSort3(is, leftMid + 1, rightMid);
				// 对第三段进行递归
				mergeSort3(is, rightMid + 1, right);
				// 合并
				merge(is, left, leftMid, rightMid, right);
			}else if(left < right){
				int middle = (left + right) / 2;

				// 对左边进行递归
				mergeSort(is, left, middle);
				// 对you边进行递归
				mergeSort(is, middle + 1, right);
				// 合并
				merge(is, left, middle, right);
			}
		}
	}
private void merge(int[] is, int left, int leftMid, int rightMid, int right) {
		int[] tempArr = new int[MAX];
		// 第二个数组的起始位置
		int mid = leftMid + 1;
		int mid2 = rightMid + 1;
		int tem = left;
		int temp = left;
		// 循环遍历,从三个数组中找出较小的放入中间数组
		while (left <= leftMid && mid <= rightMid && mid2 <= right) {
			if (is[left] <= is[mid] && is[left] <= is[mid2]) {
				tempArr[temp++] = is[left++];
			} else if (is[mid] <= is[mid2]) {
				tempArr[temp++] = is[mid++];
			} else {
				tempArr[temp++] = is[mid2++];
			}
		}
		/**
		 * 本别考虑剩下的几种情况(剩下两个分数组,剩下哪段都可能)
		 */
		// 这是剩下第一段和第二段的情况
		while (left <= leftMid && mid <= rightMid) {
			if (is[left] <= is[mid]) {
				tempArr[temp++] = is[left++];
			} else {
				tempArr[temp++] = is[mid++];
			}
		}
		// 这是剩下第三段和第二段的情况
		while (mid <= rightMid && mid2 <= right) {
			if (is[mid] <= is[mid2]) {
				tempArr[temp++] = is[mid++];
			} else {
				tempArr[temp++] = is[mid2++];
			}
		}
		// 这是剩下第一段和第三段的情况
		while (left <= leftMid && mid2 <= right) {
			if (is[left] <= is[mid2]) {
				tempArr[temp++] = is[left++];
			} else {
				tempArr[temp++] = is[mid2++];
			}
		}

		// 将剩余数组里面的数放入临时数组

		// 最后剩下第一段
		while (left <= leftMid) {
			tempArr[temp++] = is[left++];
		}
		// 最后剩下第二段
		while (mid <= rightMid) {
			tempArr[temp++] = is[mid++];
		}
		// 最后剩下第三段
		while (mid2 <= right) {
			tempArr[temp++] = is[mid2++];
		}
		// 将临时数组拷贝回原数组
		while (tem <= right) {
			is[tem] = tempArr[tem++];
		}
		// System.out.println(tem+""+right);
		// System.arraycopy(tempArr, tem, is, tem, (right - tem + 1));
	}

 3路归并的情况下,我知道是不比二路归并快的,因为虽然合并的次数减少了,但是明显比较和移动的次数增加很多。代码检查是没发现问题的,但是3路归并排序1000000个乱序数组,耗时高达100秒,是两路归并的500倍。。。   肯定是我代码有问题,希望有大神可以赐教。

        另外实现一下两路归并的两个线程,发现在100万数据的情况下,两线程和单线程不相上下,说明耗时短的情况下,线程开销与多线程的时间减少不相上下,但是在10000000万数据的排序中,两个线程明显比单线程要占上风。      告诉我们在数据量多的情况下,能用多线程还是用多线程吧o(∩_∩)o

       代码:

/**
	 * @Title: mergeSort
	 * @Description: TODO(两路归并算法(多线程实现))
	 * @param @param is
	 * @param @param left
	 * @param @param right 设定文件
	 * @return void 返回类型
	 */
	private void mergeSortThread(int[] is, int left, int right) {
		if (left < right) {
			int middle = (left + right) / 2;
			// 对左边进行递归
			merageThread t1 = new merageThread(left, middle);
			// mergeSort(is, left, middle);
			// 对右边进行递归
			merageThread t2 = new merageThread(middle + 1, right);
			t1.start();
			t2.start();

			/**
			 * 因为合并要用到上面归并的结果,所以合并要等到归并结束之后才能进行
			 */
			try {
				t1.join();
				t2.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// mergeSort(is, middle + 1, right);
			// 合并
			merge(is, left, middle, right);
		}
	}
/**
	 * @ClassName: merageThread 归并排序进程
	 * @Description: TODO(这里用一句话描述这个类的作用)
	 * @author xuejupo [email protected]
	 * @date 2015-11-9 下午6:37:17
	 * 
	 */
	static class merageThread extends Thread {
		private int left;
		private int right;

		public merageThread(int left, int right) {
			this.left = left;
			this.right = right;
		}

		public void run() {
			mergeSort(is, left, right);
		}

		/**
		 * @Title: mergeSort
		 * @Description: TODO(两路归并算法)
		 * @param @param is
		 * @param @param left
		 * @param @param right 设定文件
		 * @return void 返回类型
		 */
		private void mergeSort(int[] is, int left, int right) {
			if (left < right) {
				int middle = (left + right) / 2;
				// 对左边进行递归
				mergeSort(is, left, middle);
				// 对右边进行递归
				mergeSort(is, middle + 1, right);
				// 合并
				merge(is, left, middle, right);
			}
		}

		private void merge(int[] is, int left, int middle, int right) {
			// 右边的起始位置
			int mid = middle + 1;
			int tem = left;
			int temp = left;
			// 循环遍历,从两个数组中找出较小的放入中间数组
			while (left <= middle && mid <= right) {
				if (is[left] <= is[mid]) {
					tempArr[temp++] = is[left++];
				} else {
					tempArr[temp++] = is[mid++];
				}
			}
			// 将剩余数组里面的数放入临时数组
			while (left <= middle) {
				tempArr[temp++] = is[left++];
			}
			while (mid <= right) {
				tempArr[temp++] = is[mid++];
			}
			// 将临时数组拷贝回原数组
			while (tem <= right) {
				is[tem] = tempArr[tem++];
			}
			// System.arraycopy(tempArr, tem, is, tem, (right - tem + 1));

		}

	}
	
	/**
	 * @ClassName: merageThread 归并排序多线程算法
	 * @Description: TODO(这里用一句话描述这个类的作用)
	 * @author xuejupo [email protected]
	 * @date 2015-11-9 下午6:37:17
	 * 
	 */
	static class merageThread2 extends Thread {
		private int left;
		private int right;

		public merageThread2(int left, int right) {
			this.left = left;
			this.right = right;
		}

		public void run() {
			mergeSort(is, left, right);
		}

		/**
		 * @Title: mergeSort
		 * @Description: TODO(两路归并算法)
		 * @param @param is
		 * @param @param left
		 * @param @param right 设定文件
		 * @return void 返回类型
		 */
		private void mergeSort(int[] is, int left, int right) {
			if (left < right) {
				int middle = (left + right) / 2;
				// 对左边进行递归
				merageThread2 t1 = new merageThread2(left, middle);
				merageThread2 t2 = new merageThread2(middle + 1, right);
				t1.start();
				t2.start();
				try {
					t1.join();
					t2.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				// 合并
				merge(is, left, middle, right);
			}
		}

		private void merge(int[] is, int left, int middle, int right) {
			// 右边的起始位置
			int mid = middle + 1;
			int tem = left;
			int temp = left;
			// 循环遍历,从两个数组中找出较小的放入中间数组
			while (left <= middle && mid <= right) {
				if (is[left] <= is[mid]) {
					tempArr[temp++] = is[left++];
				} else {
					tempArr[temp++] = is[mid++];
				}
			}
			// 将剩余数组里面的数放入临时数组
			while (left <= middle) {
				tempArr[temp++] = is[left++];
			}
			while (mid <= right) {
				tempArr[temp++] = is[mid++];
			}
			// 将临时数组拷贝回原数组
			while (tem <= right) {
				is[tem] = tempArr[tem++];
			}
			// System.arraycopy(tempArr, tem, is, tem, (right - tem + 1));

		}

	}

 100万数据的排序结果:

线程执行了129毫秒
普通程序执行了153毫秒

 这个结果不是一定的,有时候多线程方法耗时反而多余普通的排序方法。

但是当1000万数据时,两个线程耗时永远低于单线程。

线程执行了1175毫秒
普通程序执行了1510毫秒

猜你喜欢

转载自709002341.iteye.com/blog/2255803