八大排序算法
冒泡排序(暴力排序)、选择排序、快速排序、归并排序(后序排序)、链式基数排序、插入排序、希尔排序、堆排序。
拥有相关性的排序:
(选择排序、快速排序);(插入排序、希尔排序);
时间复杂度和空间复杂度。
例子:for循环n次, for中执行的代码为单元。
时间复杂度:n循环的次数 O(n);
空间复杂度:此循环所占存储器上存储空间,复杂度:O(n)即 一个单元所占空间 * n ; (如果for中单元为常量则空间是固定的,复杂度:O(1))
分治法:
顾名思义,分而治之。将一个大的整体拆分为若干个小元素。前提是,不管整体还是小元素,其执行流程应该不变。如二分查找,就是每次从中间开始分,直到找到最终元素,其比较的流程不变。
* 冒泡排序(暴力排序)
适用:适用于小数据,8个以内数据最快。短处:不适用与大量数据,资源消耗过多。时间复杂度和空间复杂度过多。
原理+步骤:
相邻数据进行比较,按大或小,进行交换,类似气泡,从头到尾逐渐排序。
1.相邻数据进行比较,按大或小,进行交换
2.然后指针指向下一位,重复步骤1,最后是最大或最小值排列在队尾部
3.去掉已排列的数据,重复1,2 最终所有数据都已完成排序为止
/**
* 冒泡排序,相邻数据比较,交换
*/
public void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
boolean flag = true;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] < array[j + 1]) {
int x = array[j];
array[j] = array[j + 1];
array[j + 1] = x;
flag = false;
}
}
if (flag) {
break;
}
}
}
解读代码:冒泡排序有很多写法,都很简单。
这里flag的创建,为了减少排列的次数。因为每次排序都是从0到array.length-1-i 开始 ,当没有交换发生时,说明已经完全排序好了,那么如果外层循环有剩余,就直接退出,不再进行比较排序了。
* 选择排序
适用:适用于小数据,8个以内数据最快。采用遍历数组,找到最小或最大数据的下标,然后交换。短处:不适用于大数据
原理+步骤:
采用遍历数组,找到最小或最大数据的下标,然后交换。
1.采用遍历数组,找到最小或最大数据的下标
2.记录初始位置下标,然后交换。
3.初始下标++,重复1,2。直至完成排序。
/**
* 选择排序
*/
public void selectSort(int[] array) {
int index;
int target;
for (int i = 0; i < array.length; i++) {
index = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[index]) {
index = j;
}
}
if (index != i) {
target = array[index];
array[index] = array[i];
array[i] = target;
}
}
// int index ;
// int j;
//
// int target;
// for (int i = 0; i < array.length; i++) {
// target = array[i];
// index = i;
// j = i+1;
// while (j<array.length){
// if (array[j] > array[index]){
// index = j;
// }
// j ++;
// }
// array[i] = array[index];
// array[index] = target;
// }
}
* 快速排序 简单明了的解释
适用:适用于大量数据,线性结构 短处:数据大量重复时性能不好,不用于单向链式结构。
原理+步骤:
采用分治法,设定low 和 high两个指针,每当一个元素位置定位成功后,递归调用,low,high重新设定,选择排序的加强版,从头和尾部各一个指针进行排序,从头取第一个数据,那么从尾部开始遍历,每次找到对应大小数据就变化遍历位置,直到所有数据排序完成。
1.设定low 和 high两个指针,取头数据,从尾部遍历,即high指针位置
2.如果数据可交换,则将此时指针位置数据设置到另一指针位置。然后指针变换。直到最后位置确定。如:
3.递归,重新设定low和high,并将此过程分为两段,重复1,2,3。直到所有数据完成排序。
/**
* 快速排序
* 双指针,分治法
* 1.取第一个数 从首尾
* 2.按取数位置从反向开始遍历
* 3.将符合的数据填入指针所在位置,变换方向
* 4.递归直到排序完成
*/
public void quickSort(int[] array, int begin, int end) {
if (end - begin <= 0) return;
int low = begin;
int high = end;
boolean orientation = true;
int target = array[low];
l1:
while (low < high) {
if (orientation) {
for (int i = high; i > low; i--) {
if (target >= array[i]) {
array[low] = array[i];
low++;
high = i;
orientation = !orientation;
continue l1;
}
}
high = low;
} else {
for (int i = low; i <= high; i++) {
if (target <= array[i]) {
array[high] = array[i];
high--;
low = i;
orientation = !orientation;
continue l1;
}
}
low = high;
}
}
//此时low = high
array[low] = target;
quickSort(array, begin, low - 1);
quickSort(array, high + 1, end);
}
解读代码:
如以上代码所示,设定boolean进行方向处理。完成一个数的定位后,记录这个数的位置,然后递归从此数两端开始重新处理。
* 归并排序(后序排序)
适用:适用数据量大,复用多,链式结构 短处是:空间占用多
原理+步骤:
采用分治法,将若干个元素分为最小单元。从最小单元开始向上层治理。如图:
1.创建底层比较方法。
2.创建合并的递归方法。
3.执行递归方法。
/**
* 归并排序,分治法
* left 和 right 都是下标
*/
public void mergeSort(int[] array, int left, int right) {
if (right - left <= 0) return;
int mid = (left + right) / 2;
//这里找到中点,那么左端是 left - mid, 右端是 mid+1 - right
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
//递归到最后的最小单位就是 两个数 此时 left=0,right=1,mid = 1
merge(array, left, mid + 1, right);
}
public void merge(int[] array, int left, int mid, int right) {
int leftSize = mid - left;
int rightSize = right - mid + 1;
int[] leftArray = new int[leftSize];
int[] rightArray = new int[rightSize];
for (int i = 0; i < leftSize; i++) {
leftArray[i] = array[left + i];
}
for (int i = 0; i < rightSize; i++) {
rightArray[i] = array[mid + i];
}
int i = 0, j = 0;
int k = left;
while (i < leftSize && j < rightSize) {
if (leftArray[i] < rightArray[j]) {
array[k] = leftArray[i];
k++;
i++;
} else {
array[k] = rightArray[j];
k++;
j++;
}
}
while (i < leftSize) {
array[k] = leftArray[i];
k++;
i++;
}
while (j < rightSize) {
array[k] = rightArray[j];
k++;
j++;
}
}
代码解读:
比较的方法的底层元素不只两个。因为从底到顶,最后的元素是 4 - 4,即:
最后是两个有序数组比较。
* 链式基数排序
适用:关键字排序或麻将,扑克等分类排序处理。短处:不适用大数据,链式处理导致内存占用多。
原理+步骤:
排序有点像HashTable,链式结构存储数据然后遍历出来就是排序好的数据,按同类型分,相同类型用链式存储。应用场景:如关键字排序。
例子:链表基数: 1 2 3 4 类,1 中建立链表: a 指向 b ,b 指向 c ,按序形成链表。
麻将的例子:
1.按大小分类,建立链表
2.在1的基础上找出的数组再按类型建立链表
3.此时就是已经有序的数据。
此次代码,我只做了按顺序设置链表,并未做分类。代码步骤如下
1.创建链表数据类
2.按大小进行位置插入
3.从根节点开始遍历到最后,此时的数据就是排序好的
/**
* 大多用于分类
* 如麻将,用到大小和类型
* 步骤:
* 1.按大小创建链表
* 2.在1的基础上,按类型分,创建一次链表
* <p>
* 此时数据就是按筒 万 条,三种类型分出,并排序好了
*
*
* 这里多说一点的是:链式基数排序,主要在于先分基数然后按链式结构处理
* 如:我们排序很多整数数据时,可以按尾数0-9来分类,再按大小分类 或是 按位数来分,按大小再分。这只是一个例子。分类就是基数
*
* 上面的麻将的例子就是:类型分创建链表,然后大小再分创建链表。最后取值就是按类型有序的
*
*
* 下面的代码是按大小创建链表的例子。
*
* @param array
* @param start
* @param end
*/
public void linkSort(int[] array, int start, int end) {
int linkSize = end - start;
for (int i = 0; i < linkSize; i++) {
insertLink(array[start+i]);
}
LinkData linkData = root;
int i = 0;
while (linkData!=null){
array[i] = linkData.data;
i++;
linkData=linkData.next;
}
}
LinkData root;
public void insertLink(int data) {
LinkData linkData = new LinkData();
linkData.data = data;
if (root == null) {
root = linkData;
return;
}
LinkData lastData = root;
while (true) {
if (lastData.data <= data) {
if (lastData.pre!=null){
lastData.pre.next = linkData;
}else {
root = linkData;
}
linkData.pre = lastData.pre;
linkData.next = lastData;
lastData.pre = linkData;
return;
}
if (lastData.next == null) {
lastData.next = linkData;
linkData.pre = lastData;
return;
} else {
lastData = lastData.next;
}
}
}
class LinkData {
LinkData next;
LinkData pre;
int data;
}
* 插入排序
适用:大数据且重复比较多时。 短处:数据大重复少时,排序速度相对插入排序慢。数据小时,速度慢。
原理+步骤:
核心是:多比较少交换,减小资源消耗。
其和选择排序有点相似,但是插入排序是从整个数组前面开始遍历,从后面开始向前对比,如有较大或较小数据,则将对比位置的数据设置到当前位置。最后将最后位置的数据设置为标记数据。
1.从当前位置与前一位对比,符合交换就将前一位数据后移,记录前一位数据交换前的位置
2.重复步骤1,直到数据排列完成。
/**
* 简单插入排序
*
* @param array
*/
public void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int j = i;
int target = array[j];
while (j > 0 && array[j - 1] > target) {
array[j] = array[j - 1];
j--;
}
array[j] = target;
}
}
代码解读:
分为外层循环和内存循环。
外层循环用于:加入数据
内存循环用于交换位置,找到符合数据插入的位置。并后移插入位置之后的所有数据
* 希尔排序 (插入排序升级版,且其最后一次执行时必须执行步长为1 的基础插入排序)
适用:使用于大量数据,比插入和堆排序快,但大量数据时比快速排序慢,重复数据多时比快速排序适用。
短处:小数据不适用,大量数据重复少时不如快速排序
原理+步骤:
插入排序的升级版 ,采用步长的策略,设定步长,进行插入排序,但是最后都要走一遍步长为1的基础插入排序。由于之前的步长策略,进一步减小了交换,减少资源消耗。
其步骤和插入一样,不过增加了步长。
/**
* 1 3
* 希尔排序
* 插入排序的增强版,增加步长设置
* <p>
* 1.确定循环次数为 步长
* 2.走简单排序,但是每次查找的数据以步长为单位而不是 1
* <p>
* 调用时最后一步必须把步长设置为1
*/
public void shellSort(int[] array, int stepSize) {
for (int k = 0; k < stepSize; k++) {
//这里走简单插入排序
for (int i = 0; i < array.length; i++) {
int j = i;
int target = array[j];
while (j > stepSize - 1 && array[j - stepSize] < target) {
array[j] = array[j - stepSize];
j = j - stepSize;
}
array[j] = target;
}
}
}
调用时:最后一步必须步长为 1,执行基础插入排序
int[] array =new int[]{2, 5, 3, 1, 0, 8, 16, 20, 15, 14};
shellSort(array,4);
shellSort(array,1);
* 堆排序
适用:超大数据中查找前n个数据时。短处:仅仅适用于数据特别大,且只找寻前n个数据时。其他情况速度慢而且时间长。
原理+步骤:
利用堆这种数据结构进行排序,堆结构类似完全二叉树,满足堆积的性质即其子节点的数据总是小于或大于父节点。堆排序,就是每次建立堆,然后取出根节点。
父节点必须比其子节点大或小,并以此调整堆。
需知:当前节点为k,则父节点为(k-1)/2,左孩子为(2*k)+1,右孩子为(2*k)+2。
1.建立堆排序及调整方法
2.建堆,循环调整。
3.将堆中根节点与最后一个节点交换。然后再重复步骤2,3。最后结果遍历出来的数据就是有序的。
/**
* 调整堆
*/
public void maxHeapify(int[] array, int start, int end) {
int dad = start;//当前开始节点开始调整
int son = dad * 2 + 1;//左孩子
while (son <= end) {
if (son + 1 <= end && array[son + 1] > array[son]) {
son++;
}
if (array[son] > array[dad]) {
int temp = array[dad];
array[dad] = array[son];
array[son] = temp;
dad = son;//当前开始节点开始调整
son = dad * 2 + 1;//左孩子
} else {
return;
}
}
}
/**
* @param array
* @param len 是排序的起点,是从数组后往前排序,len-1 是数组下标
*/
public void heapSort(int[] array, int len) {
//建堆,堆是一个近似完全二叉树的数据结构所以可通过 (k - 1)/ 2 找到父节点
//len的下标是len-1 ,所以其父节点是(len - 1 - 1)/2 = len/2 -1
for (int i = len / 2 - 1; i >= 0; i--) {
maxHeapify(array, i, len - 1);
}
//建堆和排序后,将根节点与最后的叶子节点交换
for (int i = len - 1; i >= 0; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
maxHeapify(array, 0, i - 1);
}
}
解读代码:
堆排序,就是从传入节点位置开始向其子节点进行调整,如果左孩子和右孩子中的最大值比父节点大,那么就需要调整,交换父节点和其孩子,并判断交换后对孩子的子节点是否有影响,有影响就要继续调整。
总结:此文中代码都已经经过测试。八大排序点的原理就写到这了。都是我的个人理解。有不对的地方请指出,谢谢。希望大家能看的明白~
TEST CODE
public void test() {
int[] array =new int[]{2, 5, 3, 1, 0, 8, 16, 20, 15, 14};
// quickSort(array,0,array.length-1);
// bubbleSort(array);
// selectSort(array);
// insertSort(array);
// mergeSort(array,0,array.length-1);
// shellSort(array,4);
// shellSort(array,1);
// heapSort(array,array.length);
linkSort(array,0,array.length);
for (int i :array){
Log.e("quickSort", "-> "+i );
}
}
全部代码
import android.util.Log;
/**
* <p>
* 八大排序算法
* <p>
* 冒泡排序(暴力排序) 适用于小数据,8个以内数据最快
* 选择排序 适用于小数据,8个以内数据最快。采用遍历数组,找到最小或最大数据的下标,然后交换。
* 快速排序 采用分治法,设定low 和 high两个指针,每当一个元素位置定位成功后,递归调用,low,high重新设定,选择排序的加强版,从头和尾部各一个指针进行排序,从头取第一个数据,那么从尾部开始遍历,每次找到对应大小数据就变化遍历位置,直到所有数据排序完成
* 适用于大量数据,线性结构 短处:数据大量重复时性能不好,不用于单向链式结构
* 归并排序(后序排序) 采用分治法,将若干个元素分为最小单元,适用数据量大,复用多,链式结构 短处是:空间占用多
* 链式基数排序: 排序有点像HashTable,链式结构存储数据然后遍历出来就是排序好的数据,按同类型分,相同类型用链式存储。应用场景:如关键字排序。
* ps : 1 2 3 4 类,1 中建立链表: a 指向 b ,b 指向 c ,按序形成链表
* <p>
* 插入排序 和选择排序有点相似,但是插入排序是从整个数组后面开始向前对比,如有较大或较小数据,则将对比位置的数据设置到当前位置。最后将最后位置的数据设置为标记数据
* 多比较少交换,减小资源消耗
* 希尔排序 插入排序的升级版 ,采用步长的策略,设定步长,进行插入排序,但是最后都要走一遍步长为1的基础插入排序。由于之前的步长策略,进一步减小了交换,减少资源消耗
* 使用于大量数据,比插入和堆排序快,但大量数据时比快速排序慢,重复数据多时比快速排序适用。
* <p>
* 堆排序 利用堆这种数据结构进行排序,堆结构类似完全二叉树,满足堆积的性质即其子节点的数据总是小于或大于父节点。堆排序,就是每次建立堆,然后取出根节点。
* 应用:大数据查找前n个数据,也就是说仅仅适用于数据特别大,且只找寻前n个数据时。其他情况速度慢而且时间长。
*/
public class SortTest {
public void test() {
int[] array =new int[]{2, 5, 3, 1, 0, 8, 16, 20, 15, 14};
// quickSort(array,0,array.length-1);
// bubbleSort(array);
// selectSort(array);
// insertSort(array);
// mergeSort(array,0,array.length-1);
// shellSort(array,4);
// shellSort(array,1);
// heapSort(array,array.length);
linkSort(array,0,array.length);
for (int i :array){
Log.e("quickSort", "-> "+i );
}
}
/**
* 冒泡排序,相邻数据比较,交换
*/
public void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
boolean flag = true;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] < array[j + 1]) {
int x = array[j];
array[j] = array[j + 1];
array[j + 1] = x;
flag = false;
}
}
if (flag) {
break;
}
}
}
/**
* 选择排序
*/
public void selectSort(int[] array) {
int index;
int target;
for (int i = 0; i < array.length; i++) {
index = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[index]) {
index = j;
}
}
if (index != i) {
target = array[index];
array[index] = array[i];
array[i] = target;
}
}
// int index ;
// int j;
//
// int target;
// for (int i = 0; i < array.length; i++) {
// target = array[i];
// index = i;
// j = i+1;
// while (j<array.length){
// if (array[j] > array[index]){
// index = j;
// }
// j ++;
// }
// array[i] = array[index];
// array[index] = target;
// }
}
/**
* 快速排序
* 双指针,分治法
* 1.取第一个数 从首尾
* 2.按取数位置从反向开始遍历
* 3.将符合的数据填入指针所在位置,变换方向
* 4.递归直到排序完成
*/
public void quickSort(int[] array, int begin, int end) {
if (end - begin <= 0) return;
int low = begin;
int high = end;
boolean orientation = true;
int target = array[low];
l1:
while (low < high) {
if (orientation) {
for (int i = high; i > low; i--) {
if (target >= array[i]) {
array[low] = array[i];
low++;
high = i;
orientation = !orientation;
continue l1;
}
}
high = low;
} else {
for (int i = low; i <= high; i++) {
if (target <= array[i]) {
array[high] = array[i];
high--;
low = i;
orientation = !orientation;
continue l1;
}
}
low = high;
}
}
//此时low = high
array[low] = target;
quickSort(array, begin, low - 1);
quickSort(array, high + 1, end);
}
/**
* 简单插入排序
*
* @param array
*/
public void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int j = i;
int target = array[j];
while (j > 0 && array[j - 1] > target) {
array[j] = array[j - 1];
j--;
}
array[j] = target;
}
}
/**
* 1 3
* 希尔排序
* 插入排序的增强版,增加步长设置
* <p>
* 1.确定循环次数为 步长
* 2.走简单排序,但是每次查找的数据以步长为单位而不是 1
* <p>
* 调用时最后一步必须把步长设置为1
*/
public void shellSort(int[] array, int stepSize) {
for (int k = 0; k < stepSize; k++) {
//这里走简单插入排序
for (int i = 0; i < array.length; i++) {
int j = i;
int target = array[j];
while (j > stepSize - 1 && array[j - stepSize] < target) {
array[j] = array[j - stepSize];
j = j - stepSize;
}
array[j] = target;
}
}
}
/**
* 归并排序,分治法
* left 和 right 都是下标
*/
public void mergeSort(int[] array, int left, int right) {
if (right - left <= 0) return;
int mid = (left + right) / 2;
//这里找到中点,那么左端是 left - mid, 右端是 mid+1 - right
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
//递归到最后的最小单位就是 两个数 此时 left=0,right=1,mid = 1
merge(array, left, mid + 1, right);
}
public void merge(int[] array, int left, int mid, int right) {
int leftSize = mid - left;
int rightSize = right - mid + 1;
int[] leftArray = new int[leftSize];
int[] rightArray = new int[rightSize];
for (int i = 0; i < leftSize; i++) {
leftArray[i] = array[left + i];
}
for (int i = 0; i < rightSize; i++) {
rightArray[i] = array[mid + i];
}
int i = 0, j = 0;
int k = left;
while (i < leftSize && j < rightSize) {
if (leftArray[i] < rightArray[j]) {
array[k] = leftArray[i];
k++;
i++;
} else {
array[k] = rightArray[j];
k++;
j++;
}
}
while (i < leftSize) {
array[k] = leftArray[i];
k++;
i++;
}
while (j < rightSize) {
array[k] = rightArray[j];
k++;
j++;
}
}
/**
* 堆排序
* <p>
* 1.建堆
* 2.处理堆
* 3.拿出根节点,执行2
*/
/**
* 调整堆
*/
public void maxHeapify(int[] array, int start, int end) {
int dad = start;//当前开始节点开始调整
int son = dad * 2 + 1;//左孩子
while (son <= end) {
if (son + 1 <= end && array[son + 1] > array[son]) {
son++;
}
if (array[son] > array[dad]) {
int temp = array[dad];
array[dad] = array[son];
array[son] = temp;
dad = son;//当前开始节点开始调整
son = dad * 2 + 1;//左孩子
} else {
return;
}
}
}
/**
* @param array
* @param len 是排序的起点,是从数组后往前排序,len-1 是数组下标
*/
public void heapSort(int[] array, int len) {
//建堆,堆是一个近似完全二叉树的数据结构所以可通过 (k - 1)/ 2 找到父节点
//len的下标是len-1 ,所以其父节点是(len - 1 - 1)/2 = len/2 -1
for (int i = len / 2 - 1; i >= 0; i--) {
maxHeapify(array, i, len - 1);
}
//建堆和排序后,将根节点与最后的叶子节点交换
for (int i = len - 1; i >= 0; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
maxHeapify(array, 0, i - 1);
}
}
/**
* 大多用于分类
* 如麻将,用到大小和类型
* 步骤:
* 1.按大小创建链表
* 2.在1的基础上,按类型分,创建一次链表
* <p>
* 此时数据就是按筒 万 条,三种类型分出,并排序好了
*
*
* 这里多说一点的是:链式基数排序,主要在于先分基数然后按链式结构处理
* 如:我们排序很多整数数据时,可以按尾数0-9来分类,再按大小分类 或是 按位数来分,按大小再分。这只是一个例子。分类就是基数
*
* 上面的麻将的例子就是:类型分创建链表,然后大小再分创建链表。最后取值就是按类型有序的
*
*
* 下面的代码是按大小创建链表的例子。
*
* @param array
* @param start
* @param end
*/
public void linkSort(int[] array, int start, int end) {
int linkSize = end - start;
for (int i = 0; i < linkSize; i++) {
insertLink(array[start+i]);
}
LinkData linkData = root;
int i = 0;
while (linkData!=null){
array[i] = linkData.data;
i++;
linkData=linkData.next;
}
}
LinkData root;
public void insertLink(int data) {
LinkData linkData = new LinkData();
linkData.data = data;
if (root == null) {
root = linkData;
return;
}
LinkData lastData = root;
while (true) {
if (lastData.data <= data) {
if (lastData.pre!=null){
lastData.pre.next = linkData;
}else {
root = linkData;
}
linkData.pre = lastData.pre;
linkData.next = lastData;
lastData.pre = linkData;
return;
}
if (lastData.next == null) {
lastData.next = linkData;
linkData.pre = lastData;
return;
} else {
lastData = lastData.next;
}
}
}
class LinkData {
LinkData next;
LinkData pre;
int data;
}
}
链式基数排序麻将的例子代码:
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
LinkedList<Mahjong> list=new LinkedList<Mahjong>();
list.add(new Mahjong(3,1));
list.add(new Mahjong(2,3));
list.add(new Mahjong(3,7));
list.add(new Mahjong(1,1));
list.add(new Mahjong(3,8));
list.add(new Mahjong(2,2));
list.add(new Mahjong(3,2));
list.add(new Mahjong(1,3));
list.add(new Mahjong(3,9));
System.out.println(list);
radixSort(list);
System.out.println(list);
}
public static void radixSort(LinkedList<Mahjong> list){
//先对点数进行分组
LinkedList[] rankList=new LinkedList[9];
for (int i = 0; i < rankList.length; i++) {
rankList[i]=new LinkedList();
}
//把数据一个个放到对应的组中
while(list.size()>0){
//取一个
Mahjong m=list.remove();
//放到组中 下标=点数减1的
rankList[m.rank-1].add(m);
}
//把9组合并在一起
for (int i = 0; i < rankList.length; i++) {
list.addAll(rankList[i]);
}
//先花色进行分组
LinkedList[] suitList=new LinkedList[3];
for (int i = 0; i < suitList.length; i++) {
suitList[i]=new LinkedList();
}
//把数据一个个放到对应的组中
while(list.size()>0){
//取一个
Mahjong m=list.remove();
//放到组中 下标=点数减1的
suitList[m.suit-1].add(m);
}
//把3个组合到一起
for (int i = 0; i < suitList.length; i++) {
list.addAll(suitList[i]);
}
}
}
public class Mahjong {
public int suit;//筒,万,索
public int rank;//点数 一 二 三
public Mahjong(int suit, int rank) {
this.suit = suit;
this.rank = rank;
}
@Override
public String toString() {
return "("+this.suit+" "+this.rank+")";
}
}
//-------------------------------关键LinkList代码--------------------------
public class LinkedList<E> {
/**
* 结点
*/
private static class Node<E> {
E item;
Node<E> prev;
Node<E> next;
public Node(Node<E> prev, E item, Node<E> next) {
this.item = item;
this.prev = prev;
this.next = next;
}
}
public LinkedList() {
}
//头节点
Node<E> first;
//尾节点
Node<E> last;
//大小
int size;
/**
* 添加数据在最后
*/
public void add(E e) {
linkLast(e);
}
/**
* 添加到最后
* @param e
*/
private void linkLast(E e) {
Node<E> newNode = new Node<E>(last, e, null);
Node<E> l = last;
last=newNode;
if(l==null){
first=newNode;
}else {
l.next = newNode;
}
size++;
}
/**
* 查找位置
*/
public E get(int index){
if(index<0 || index>size){
return null;
}
return node(index).item;
}
/**
* 获取index位置上的节点
*/
private Node<E> node(int index){
//如果index在整个链表的前半部分
if(index<(size>>1)){ //1000 100 10
Node<E> node=first;
for (int i = 0; i < index; i++) {
node=node.next;
}
return node;
}else{
Node<E> node=last;
for (int i = size-1; i > index; i--) {
node=node.prev;
}
return node;
}
}
/**
* 添加数据在index位置
*/
public void add(int index,E e) {
if(index<0 || index>size){
return ;
}
if(index==size){
linkLast(e);
}else{
Node<E> target=node(index);// index=2
Node<E> pre=target.prev;
Node<E> newNode=new Node<E>(pre,e,target);
if(pre==null){
first=newNode;
target.prev = newNode;//4
}else {
pre.next = newNode;//3
target.prev = newNode;//4
}
size++;
}
}
/**
* 删除元素
*/
public void remove(int index){
Node<E> target=node(index);
unlinkNode(target);
}
private void unlinkNode(Node<E> p) {//index=2
Node<E> pre=p.prev;
Node<E> next=p.next;
if(pre==null){
first=p.next;
}else{
pre.next=p.next;
}
if(next==null){
last=p.prev;
}else{
next.prev=p.prev;
}
size--;
}
}