个人随笔
目录
工具类:Java版九种排序算法(对数器校验版)
2020-09-11 22:57:20

排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面的排序工具类都是属于内排序。

  1. /**
  2. * 九种常用排序算法工具类:
  3. * 插入排序:直接插入o(N^2),二分插入O(N^2),希尔排序:依赖增量d
  4. * 选择排序:简单选择o(N^2),堆排序O(NlogN)
  5. * 交换排序:冒泡排序o(N^2),快速排序(随机化可到达O(NlogN))
  6. * 归并排序O(NlogN)
  7. * 基数排序O(d(n+r)),d为位数,r为基数
  8. *
  9. * 排序算法的选择
  10. * 1.数据规模较小
  11. * (1)待排序列基本序的情况下,可以选择直接插入排序;
  12. * (2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
  13. * 2.数据规模不是很大
  14. * (1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
  15. * (2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
  16. * 3.数据规模很大
  17. * (1)对稳定性有求,则可考虑归并排序。
  18. * (2)对稳定性没要求,宜用堆排序
  19. * 4.序列初始基本有序(正序)
  20. * 宜用直接插入,冒泡
  21. */
  22. public class SortUtil {
  23. /***********************************插入排序:直接插入,二分插入,希尔排序**********************/
  24. /**
  25. * 插入排序改进版,找到位置最后才插入
  26. * @param a
  27. * @return
  28. */
  29. public static int[] insertSort(int[] a) {
  30. //假设第一个记录为已经待排序好的记录,那么要比较a.length-1个记录,所以外层循环是a.length-1次
  31. //这里可以直接i=1从第二位开始处理也一样
  32. for(int i=1;i<a.length;i++) {
  33. //缓存待排序的记录
  34. int temp = a[i];
  35. int j;//这个是待插入的位置-1
  36. //跟前面已排序的记录做对比,找到合适的位置,建议从后往前面比,若是比最后一位小,则向前移动一位,否则就直接找到该位置
  37. for(j=i-1;j>=0;j--) {
  38. //如果当前待排序的数比这一个排序的数大,则跳出循环,否则交换位置
  39. if(temp>a[j]) {
  40. break;
  41. }else {
  42. //把当前记录往后面移动一位
  43. a[j+1]=a[j];
  44. }
  45. }
  46. a[j+1]=temp;
  47. }
  48. return a;
  49. }
  50. /**
  51. * 二分插入排序
  52. * @param a
  53. * @return
  54. */
  55. public static int[] divideInsertSort(int[] a) {
  56. //这里可以直接i=1从第二位开始处理也一样
  57. for(int i=1;i<a.length;i++) {
  58. //缓存待排序的记录
  59. int temp = a[i];
  60. //下面用二分法来在前面排好序的位置中找到插入的位置
  61. //找到前面已排序的起点
  62. int left = 0;
  63. //找到已排序的终点
  64. int right = i-1;
  65. //中间位置坐标
  66. int mid = 0;
  67. //如果左边小于右边,就表明数据大于1,可以用二分法
  68. while(left<=right) {
  69. //这个如果是偶数,比如4,则第二位当做中间值,若是5则第三位
  70. mid = (right+left)/2;
  71. //对比当前排序的数目和该数据的大小、
  72. if(temp>a[mid]) {
  73. //继续右边比较
  74. left = mid+1;
  75. }else {
  76. //继续左边比较
  77. right = mid-1;
  78. }
  79. }
  80. //循环结束后,left值和right值一定相同,并且比最一次比较的mid向左或者右偏移一位,所以位置就是left插入
  81. //此时必须把已排序好的从left开始往后移动
  82. //这里找到位置后,需要把该位置往后面的都右移
  83. for (int j = i-1; j >= left; j--) {
  84. a[j+1] = a[j];
  85. }
  86. if(left != i){
  87. a[left] = temp;
  88. }
  89. }
  90. return a;
  91. }
  92. /**
  93. * 希尔排序
  94. * @param a
  95. * @return
  96. */
  97. public static int[] shellSort(int[] a) {
  98. //主要是取增量d
  99. int d = a.length;
  100. while(true) {
  101. d=d/2;
  102. //开始进行分组插入排序,对d组进行排序,到最后d=1 就是一组排序
  103. for(int x=0;x<d;x++) {
  104. //每一组分别进行直接插入排序
  105. for(int i=x+d;i<a.length;i=i+d) {
  106. //缓存待排序的记录
  107. int temp = a[i];
  108. int j;//这个是待插入的位置-1
  109. //跟前面已排序的记录做对比,找到合适的位置,建议从后往前面比,若是比最后一位小,则向前移动一位,否则就直接找到该位置
  110. for(j=i-d;j>=0;j=j-d) {
  111. //如果当前待排序的数比这一个排序的数大,则跳出循环,否则交换位置
  112. if(temp>a[j]) {
  113. break;
  114. }else {
  115. //把当前记录往后面移动一位
  116. a[j+d]=a[j];
  117. }
  118. }
  119. a[j+d]=temp;
  120. }
  121. }
  122. //数组长度可能为1,那么这个值可能为0
  123. if(d == 1||d==0){
  124. System.out.println(d);
  125. break;
  126. }
  127. }
  128. return a;
  129. }
  130. /***************************选择排序:简单选择,堆排序******************************/
  131. /**
  132. * 简单选择排序
  133. * @param a
  134. * @return
  135. */
  136. public static int[] choiceSort(int[] a) {
  137. //这里每一个数都要做比较
  138. for (int i = 0; i < a.length; i++) {
  139. //假设第一个数是最小的数
  140. int min =a[i];
  141. int n=i; //最小数的索引
  142. //从后面找出最小的数,以及最小的数的位置
  143. for(int j=i+1;j<a.length;j++) {
  144. if(a[j]<min) {
  145. //最小数的值
  146. min = a[j];
  147. //最小数的位置
  148. n=j;
  149. }
  150. }
  151. //把当前的值和最小数的位置那个值替换
  152. a[n]=a[i];
  153. a[i]=min;
  154. }
  155. return a;
  156. }
  157. /**
  158. * 堆排序
  159. * @param a
  160. * @return
  161. */
  162. public static int[] heapSort(int[] a) {
  163. int arrayLength=a.length;
  164. //循环建堆
  165. for(int i=0;i<arrayLength-1;i++){
  166. //建堆
  167. buildMaxHeap(a,arrayLength-1-i);
  168. //交换堆顶和最后一个元素
  169. swap(a,0,arrayLength-1-i);
  170. }
  171. return a;
  172. }
  173. //对data数组从0到lastIndex建大顶堆
  174. private static void buildMaxHeap(int[] data, int lastIndex){
  175. //从lastIndex处节点(最后一个节点)的父节点开始
  176. for(int i=(lastIndex-1)/2;i>=0;i--){
  177. //k保存正在判断的节点
  178. int k=i;
  179. //如果当前k节点的子节点存在
  180. while(k*2+1<=lastIndex){
  181. //k节点的左子节点的索引
  182. int biggerIndex=2*k+1;
  183. //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
  184. if(biggerIndex<lastIndex){
  185. //若果右子节点的值较大
  186. if(data[biggerIndex]<data[biggerIndex+1]){
  187. //biggerIndex总是记录较大子节点的索引
  188. biggerIndex++;
  189. }
  190. }
  191. //如果k节点的值小于其较大的子节点的值
  192. if(data[k]<data[biggerIndex]){
  193. //交换他们
  194. swap(data,k,biggerIndex);
  195. //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
  196. k=biggerIndex;
  197. }else{
  198. break;
  199. }
  200. }
  201. }
  202. }
  203. //交换
  204. private static void swap(int[] data, int i, int j) {
  205. int tmp=data[i];
  206. data[i]=data[j];
  207. data[j]=tmp;
  208. }
  209. /*****************************交换排序:冒泡排序,快速排序************************/
  210. /**
  211. * 冒泡排序
  212. * @param a
  213. * @return
  214. */
  215. public static int[] bubbleSort(int[] a) {
  216. //1、两两比较,如果前者比后者者大则交换位置
  217. //2、每遍历一圈最大的数就会冒到最后,则确定了本轮比较中的最大值放到最后不动
  218. //3、循环1、2直至遍历完所有
  219. for (int i = 0; i < a.length-1; i++) {//外循环循环n-1次
  220. for (int j = 1; j < a.length-i; j++) {//内循环每一次要比较n-i次
  221. if(a[j-1]>a[j]){
  222. int temp=a[j-1];
  223. a[j-1]=a[j];
  224. a[j]=temp;
  225. }
  226. }
  227. }
  228. return a;
  229. }
  230. /**
  231. * 快速排序
  232. * @param a
  233. * @return
  234. */
  235. public static int[] quickSort(int[] a) {
  236. if(a.length>0) {
  237. quickSort(a,0,a.length-1);
  238. }
  239. return a;
  240. }
  241. private static void quickSort(int[] a, int low, int high) {
  242. if(low<high) {
  243. //选择基准元素
  244. int middle = getMiddle(a,low,high);
  245. quickSort(a, 0, middle-1);
  246. quickSort(a, middle+1, high);
  247. }
  248. }
  249. private static int getMiddle(int[] a, int low, int high) {
  250. //假设第一个是基准元素
  251. int temp = a[low];
  252. while(low<high) {
  253. //找到比基准元素小的位置
  254. while(low<high&&a[high]>=temp) {
  255. high--;
  256. }
  257. a[low] = a[high];
  258. //当队首元素小于等于tmp时,向前挪动low指针
  259. while(low<high && a[low]<=temp){
  260. low++;
  261. }
  262. a[high] = a[low];
  263. }
  264. a[low] = temp;
  265. return low;
  266. }
  267. /**************************归并排序***********************/
  268. /**
  269. * 归并排序
  270. * @param a
  271. * @param left
  272. * @param right
  273. */
  274. public static int[] mergeSort(int[] a) {
  275. return mergeSort(a, 0, a.length-1);
  276. }
  277. private static int[] mergeSort(int[] a, int left, int right) {
  278. if(left<right){
  279. int middle = (left+right)/2;
  280. //对左边进行递归
  281. mergeSort(a, left, middle);
  282. //对右边进行递归
  283. mergeSort(a, middle+1, right);
  284. //合并
  285. merge(a,left,middle,right);
  286. }
  287. return a;
  288. }
  289. private static void merge(int[] a, int left, int middle, int right) {
  290. int[] tmpArr = new int[a.length];
  291. int mid = middle+1; //右边的起始位置
  292. int tmp = left;
  293. int third = left;
  294. while(left<=middle && mid<=right){
  295. //从两个数组中选取较小的数放入中间数组
  296. if(a[left]<=a[mid]){
  297. tmpArr[third++] = a[left++];
  298. }else{
  299. tmpArr[third++] = a[mid++];
  300. }
  301. }
  302. //将剩余的部分放入中间数组
  303. while(left<=middle){
  304. tmpArr[third++] = a[left++];
  305. }
  306. while(mid<=right){
  307. tmpArr[third++] = a[mid++];
  308. }
  309. //将中间数组复制回原数组
  310. while(tmp<=right){
  311. a[tmp] = tmpArr[tmp++];
  312. }
  313. }
  314. /************************基数排序************************/
  315. /**
  316. * 基数排序
  317. * @param array
  318. * @return
  319. */
  320. public static int[] radixSort(int[] a) {
  321. //找到最大数,确定要排序几趟
  322. int max = 0;
  323. for (int i = 0; i < a.length; i++) {
  324. if(a[i]<0) {
  325. System.out.println("只能比较正整数:"+a[i]);
  326. return a;
  327. }
  328. if(max<a[i]){
  329. max = a[i];
  330. }
  331. }
  332. //判断位数
  333. int times = 0;
  334. while(max>0){
  335. max = max/10;
  336. times++;
  337. }
  338. //建立十个队列
  339. List<ArrayList> queue = new ArrayList<ArrayList>();
  340. for (int i = 0; i < 10; i++) {
  341. ArrayList queue1 = new ArrayList();
  342. queue.add(queue1);
  343. }
  344. //进行times次分配和收集
  345. for (int i = 0; i < times; i++) {
  346. //分配
  347. for (int j = 0; j < a.length; j++) {
  348. //每一次比较都把奇数相同的放在同一个队列中,Math.pow(a,b) a的b次方
  349. //如果i=2表示取第二位,那么就%100取余 然后/10取取除
  350. int x = a[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
  351. //直接去奇数位置寻找对应的列表
  352. ArrayList queue2 = queue.get(x);
  353. queue2.add(a[j]);
  354. queue.set(x,queue2);
  355. }
  356. //收集
  357. int count = 0;
  358. for (int j = 0; j < 10; j++) {
  359. while(queue.get(j).size()>0){
  360. ArrayList<Integer> queue3 = queue.get(j);
  361. a[count] = queue3.get(0);
  362. queue3.remove(0);
  363. count++;
  364. }
  365. }
  366. }
  367. return a;
  368. }
  369. public static void main(String[] args) {
  370. System.out.println("-------------用对数器来测是算法的正确性------------");
  371. int testTims = 500000;//测试次数
  372. int maxSize = 20;//最大测试容量
  373. int maxNum = 100;//最大测试数据
  374. boolean euqals = true;
  375. for (int i = 0; i < testTims; i++) {
  376. //原始数组
  377. int[] arr1 = generateRandomArray(maxSize,maxNum);
  378. int[] arr2 = copyArray(arr1);//这两个数组除了
  379. int[] arr3 = copyArray(arr1);//这两个数组除了
  380. //数值相同内存地址完全没关系,请看copyArray()方法实现
  381. //insertSort(arr2);
  382. //divideInsertSort(arr2);
  383. //shellSort(arr2);
  384. //choiceSort(arr2);
  385. //heapSort(arr2);
  386. //bubbleSort(arr2);
  387. //quickSort(arr2);
  388. //mergeSort(arr2);
  389. //这个只能比较正整数
  390. radixSort(arr2);
  391. rightSort(arr3);//用java.util.Arrays包的排序算法排序
  392. if (!isEquals(arr2,arr3)) {//比较是否相同
  393. printArray(arr1);//再次打印,程序员自己看看有没有毛病
  394. printArray(arr2);//再次打印,程序员自己看看有没有毛病
  395. printArray(arr3);//再次打印,程序员自己看看有没有毛病
  396. euqals = false;//一旦有不一样的值就设为false;
  397. break;
  398. }
  399. }
  400. System.out.println(euqals ? "Success:恭喜你!没毛病!" : "Error:抱歉,有错误" );//没错就恭喜,有错就报错
  401. }
  402. //这是一个绝对正确但是复杂度不好的方法
  403. public static void rightSort(int[] arr) {
  404. Arrays.sort(arr);
  405. }
  406. //这是一个随机样本产生器
  407. public static int[] generateRandomArray(int maxSize, int maxNum) {
  408. int[] arr = new int[(int) ((maxSize+1) * Math.random())];
  409. for (int i = 0; i < arr.length; i++) {
  410. arr[i] = (int) (Math.random()*(maxNum+1)) - (int)(Math.random()*maxNum);
  411. }
  412. return arr;
  413. }
  414. //这个是对比算法a和b方法是否相同的方法
  415. public static boolean isEquals(int[] arr1, int[] arr2) {
  416. if (arr1.length != arr2.length) {
  417. return false;
  418. }
  419. if (arr1 != null && arr2 == null || arr1 == null && arr2 != null) {
  420. return false;
  421. }
  422. for (int i = 0; i < arr1.length; i++) {
  423. if (arr1[i] != arr2[i]) {
  424. return false;
  425. }
  426. }
  427. return true;
  428. }
  429. //复制当前数组的一个样本
  430. public static int[] copyArray(int[] arr) {
  431. if (arr == null) {
  432. return null;
  433. }
  434. int[] newArray = new int[arr.length];
  435. for (int i = 0; i < arr.length; i++) {
  436. newArray[i] = arr[i];
  437. }
  438. return newArray;
  439. }
  440. //打印数组
  441. public static void printArray(int[] arr) {
  442. if(arr == null) {
  443. return;
  444. }
  445. for (int i = 0; i < arr.length; i++) {
  446. System.out.print(arr[i]+" ");
  447. }
  448. System.out.println();
  449. }
  450. }
 241

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2