程序员内功— —数据结构与算法之数组(2)--排序与查找

前言:

        折腾完了上一篇的增删改查,我们要往下一步走了。这一步可能相对难理解一些。但是,偏偏这些企业却很喜欢来考验面试者。哎,在群里看了几位大佬的面试,基本都是在考验我们的基础,基础稳了,就算难得那些不会也可以拿到offer。但是遗憾的是很多人都过度追求框架,对基础却有些忽略。犯了大忌,特别是坑爹的面试官,一来就直接上算法,什么二分查找,冒泡排序,选择排序等,不要钱的来,能不能好好交谈了。

        不废话了,干活干活,撸起袖子就是撸代码,秀面试官一脸!

                                    java数据结构与算法之排序和查找:


package com.ern.demo;

/**
 * 
 * @author wen
 *
 */
public class MyArray {

	private static int[] a;

	public MyArray() {
		  // 4 8 9 0 5 3 6 2 1
		a = new int[10];
		a[0] = 4;
		a[1] = 8;
		a[2] = 9;
		a[3] = 0;
		a[4] = 5;
		a[5] = 3;
		a[6] = 6;
		a[7] = 2;
		a[8] = 1;
		a[9] = 7;

	}

	

	/**
	 * 二分查找(递归方式进行)
	 * 
	 * @param args
	 * @param value
	 * @param low
	 * @param height
	 * @return
	 */
	public int selectTwo(int[] args, int value, int low, int height) {
		// 查找前先排序
		ArraySort3(args, low, height);
		// 先判断输入的值是否符合规矩
		if (value < args[low] || value > args[height] || low > height) {
			return -1;
		}

		/**
		 * 找到中间值 例如10个数字,0 1 2 3 4 5 6 7 8 9 除以2找到中间的数( 0+9)/2=4
		 */
		int middle = (low + height) / 2;
		/**
		 * 获取到中间的值,判断输入的值是大于还是小于数组中间的值 如果大于,往右边走,小于就往左走
		 * 
		 * 例如: 10个数字,0 1 2 3 4 5 6 7 8 9 (1)小编我选7 (2)第四个即中间值是3
		 * (3)判断是否大于输入的值7,是的话往右走,不是往左走 (4)递归后接下来就是 的中间值是(1+9)/2=5
		 * (5)判断是否大于输入的值7,是的话往右走,不是往左走 (6)。。。。以此类推找到
		 */
		if (args[middle] > value) {
			return selectTwo(args, value, low, height - 1);
		} else if (args[middle] < value) {
			return selectTwo(args, value, low + 1, height);
		} else {
			return middle;
		}

	}

	/**
	 * 二分查找(非递归方式进行)
	 * 
	 * @param args
	 * @param value
	 * @param low
	 * @param height
	 * @return
	 */

	public int selectTwo2(int[] args, int value, int low, int height) {
		ArraySort3(args, low, height);

		if (value < args[low] || value > args[height] || low > height) {
			return -1;
		}
		// 原理和上面的一样。。只是不用递归的方法实现
		while (low <= height) {

			int middle = (low + height) / 2;
			if (args[middle] < value) {
				low = middle + 1;
			} else if (args[middle] > value) {
				height = middle - 1;
			} else {
				return middle;

			}

		}

		return 0;
	}

	/**
	 * 查找相关元素的索引,考虑到相同元素
	 * 
	 * @param args
	 * @param value
	 * @return
	 * @throws Exception
	 */
	public int[] select(int[] args, int value) throws Exception {
		/**
		 * 遍历寻找有多少个相同,记录起来!!
		 */
		int count = 0;

		for (int i = 0; i < args.length; i++) {
			if (args[i] == value) {
				count++;
			}
		}
		/**
		 * 如果没有这个元素,就抛个异常玩玩
		 */
		if (count == 0) {
			throw new Exception("没有找到相关元素");
		}
		/**
		 * 用来返回给他存在几个元素
		 */
		int[] f = new int[count];
		// 用于数组增加
		int flength = 0;

		/**
		 * 再此遍历,找到相关索引,将他放入临时数组中
		 */
		for (int i = 0; i < args.length; i++) {
			if (args[i] == value) {
				f[flength] = i;
				flength++;
			}
		}
		return f;

	}

	/**
	 * 
	 * 直接排序====== 将数组排序
	 * 
	 * @param args
	 */
	public void ArraySort(int[] args) {
		// 用一个临时变量,用来做中间人
		int temp = 0;
		for (int i = 0; i < args.length; i++) {
			/**
			 * 这里的j=i+1的意思是,外层循环是第一个元素 不可能在和资金比较吧?
			 * 
			 * 例如: 0 1 2 3 4 外层循环拿到数组里的第一个值是0 然后第二层循环,我们不可能还用0 和 0
			 * 比较是吧?没意义,所以我们i+1
			 * 
			 */
			for (int j = i + 1; j < args.length; j++) {
				/**
				 * 判断第一个是否大于后面的9个,是对换。外层循环就是控制第几个来比较! 例如: 4 5 1 6 3
				 * 
				 * 外层循环拿到第一个 4 内层循环目的是 4 和 后面的 5 1 6 3 比较 如果 4大于 后面任何一个,就对换
				 * 第一次与后面对换 1 5 4 6 3 第二次与后面对换 1 5 4 6 3 。。。 都没有,就跳过,进入第二层循环
				 * 第一层结束后总体是: 1 5 4 6 3
				 * 
				 * 接下来拿出第二个是5 第一次与后面对换 1 4 5 6 3 第二次与后面对换 1 3 5 6 4 。。。 以此类推。。。
				 */
				if (args[i] > args[j]) {
					temp = args[i];
					args[i] = args[j];
					args[j] = temp;
				}

			}
		}
		/**
		 * 遍历出排序后所有元素
		 */
		for (int i = 0; i < args.length; i++) {
			System.out.print(args[i] + " ");
		}

	}

	/**
	 * 冒泡排序
	 * 
	 * @param args
	 */
	public void ArraySort1(int[] args) {

		/**
		 * 
		 */
		for (int i = 0; i < args.length; i++) {

			for (int j = 0; j < args.length - i - 1; j++) {
				/**
				 * 冒泡排序真的特别无语,你们会懂的~~!!!
				 * 
				 * 原理:第一个和第二个相比,第二个和第三个相比,以此进行排序, 排完后每轮会减掉最后一个。因为都最大了,,没必要在排序了吧
				 * 
				 * 例如开始是这样滴: 8 7 9 0 6 3 5 1 2 第一次排序后 8和7交换了位置 : 7 8 9 0 6 3 5 1
				 * 2 第二次排序后 不变 第三次排序后 8和9交换了位置 : 7 8 0 9 6 3 5 1 2 以此类推,,,
				 * 
				 * 外层第一轮结束后为: 7 8 0 6 3 5 1 2 9 第二轮开始:7 8 0 6 3 5 1 2 9
				 * 
				 * 第一次排序后 8和0交换了位置 : 7 0 8 6 3 5 1 2 9 第二次排序后 8和6交换了位置 : 7 0 6 8
				 * 3 5 1 2 9 第三次排序后 8和3交换了位置 : 7 0 6 3 8 5 1 2 9 。。。以此类推
				 * 
				 * 效率杠杆滴。。。但是作为经典排序。忍忍好了
				 * 
				 */
				if (args[j] > args[j + 1]) {
					int temp = args[j];
					args[j] = args[j + 1];
					args[j + 1] = temp;
				}
			}
		}
		/**
		 * 遍历数组并输出
		 */
		for (int i = 0; i < args.length; i++) {
			System.out.print(args[i] + " ");
		}

	}

	/**
	 * 选择排序(用于优化冒泡排序)
	 * 
	 * @param args
	 */
	public void ArraySort2(int[] args) {

		for (int i = 0; i < args.length; i++) {
			/**
			 * 默认相信第一个是最小的
			 */
			int temp = args[i];
			int index = i;
			/**
			 * 遍历交换,如果找到比第一个还小的,记录下交换位置的索引和值
			 */
			for (int j = i + 1; j < args.length; j++) {
				if (temp > args[j]) {
					temp = args[j];
					index = j;
				}
			}
			/**
			 * 这里做交换,index!=i的原因是如果最后一个了
			 * 最后一个index和i是相等的了。。相等了就算了吧。比也没意思了
			 */
			if (index != i) {
				args[index] = args[i];
				args[i] = temp;
			}
		}

		for (int i = 0; i < args.length; i++) {
			System.out.print(args[i] + " ");
		}
	}

	/**
	 * 快速排序(不是特别懂)
	 * 
	 * 原理是拆分,分而治之。这样速度回快很多。例如有 10 20 50 30 29 我们以50为拆分点。拆分成2部分,一部分是10 20 一部分是30
	 * 29
	 * 
	 * @param args
	 *            传入的数组
	 * @param low
	 *            最小,默认为0
	 * @param height
	 *            最大,应该为数组的长度
	 */
	public void ArraySort3(int[] args, int low, int height) {

		int i, j, index;

		if (low > height) {
			return;
		}
		i = low;
		j = height;
		
		/**
		 * 默认拿的第一个为最小
		 */
		index = args[i];
		while (i < j) {
			/**
			 * 例子:    4 8 9 0 5 3 6 2 1
			 * 第一个while时   i=0  j=8 index==args[0]==4
			 * 第一轮    j=8
			 * 		1 8 9 0 5 3 6 2 1	
			 * 		i=1
			 * 		之后是8大于4所以不走。
			 * 		i=1 j=8 所以
			 * 		j=7 i =1 args[8]=8
			 * 
			 * 继续这么一步步的切,分成2个部分,每个部分在分。。。一下就排序好了。
			 */
			
			while (i < j && args[j] >= index) {
				j--;
			}
			if (i < j) {
				args[i++] = args[j];
			}

			while (i < j && args[i] < index) {
				i++;
			}
			if (i < j) {
				args[j--] = args[i];
			}

		}

		args[i] = index;
		ArraySort3(args, low, i - 1);

		ArraySort3(args, i + 1, height);
	}

	public static void main(String[] args) {
		MyArray array = new MyArray();
		array.ArraySort3(a,0,a.length-1);
		for (int i = 0; i < a.length; i++) {
			
			System.out.print(a[i]+" ");
		}

	}

}
        上面的原理与执行过程,例子都有举出来。这里仅仅提供一个思路,如果有更好的方法欢迎私信小编。小编就没有画图来演示了。第一嫌麻烦。第二练习看文字来理解的功底。第三小编懒呀。。


总结:

        冒泡排序的原理就是对比后兑换,而且执行N多次,效率杠杠滴低,无言以对。直接排序就是对比,然后两个互相换位置,这个还是比较好理解的。选择排序是默认选择一个作为最小,然后和其他对比调换位置,和直接差不多。快速排序这个玩意,气人就算了而且还是比较难理解的,它采用的方法是分而治之,就像是微服务,将一个大的整体分成一部分一部分拆出来。这样排序的效率就真的是杠杠滴。

        二分查找的原理简单,就是取中间人(前提是排好序了),然后不断取中间的值来对比,相对来说比从到到尾撸一遍的循环好很多。


程序人生,与君共勉!


猜你喜欢

转载自blog.csdn.net/weixin_41622183/article/details/80505492