一、冒泡排序
int score[] = {67,100,50,99,87,89,90,75};
boolean flag = false;
/**
* 1、length方法用于获取数组的长度。
* 2、而length()用于获取String字符串中字符的个数。
* 第一轮计算的是需要几趟排序才能排完
*/
for (int i=0;i<score.length-1;i++){
//第二轮这个计算的是一趟排序内需要比较几次才能比较完
for (int j=0;j<score.length-1-i;j++){
if (score[j] > score[j+1]){
flag = true;
int number = score[j];
score[j] = score[j+1];
score[j+1] = number;
}
}
if (!flag){
//说明一次一次比较都没有发生,已经排好序了
break;
} else{
//说明至少排过一次序,重置标志才能有效
flag = false;
}
}
二、选择排序
int score[] = {67,100,50,99,87,89,90,75};
selectSort(score);
System.out.println(Arrays.toString(score));
}
private static void selectSort(int[] arr) {
//一共有几轮,比当前数组的长度少一轮
for (int i = 0; i < arr.length - 1; i++) {
/**
* 用于暂存数据,两个功能
* 一、比如:假设第一个是最小的,则记录第一个下标以及数据,实际第一个可能不是最小的
* 等到循环比较以后,第一个就变成最小的,则进行第二个数比较,取得最小值。
* 可以说是记录比较到了第几位
* 二、将暂存那个比假设的那个值要大的数,用于后续进行交换
*/
int midIndex = i;
int mid = arr[i];
//已经假设第一个是最小的,所以从第二个开始,与第一个进行比较
for (int j = i + 1; j < arr.length ; j++) {
//如果想按从大到小排序,可以把这里变成<,这里的比较要么去mid,要么取arr[midIndex],
//这是因为比较之后最小的元素以及下标会放到这里,是会动态变化的,不能取arr[0]
if (mid > arr[j]) {
//将比假设值要大的那个元素以及元素下标付给中间变量进行存储
midIndex = j;
mid = arr[j];
}
}
//等于的话,就相当于没有交换,本身就是最小的
if (midIndex != i) {
/**这下面两部不能反过来,否则会出错,因为原先大的元素已经放入mid这个中间值里面了
* 原先位置已经空出来了,所以要把arr[i]先放过去,然后再发mid中间值放入arr[i]中,
*/
//将原来假设小的放到后面,因为假设小的不是最小的,这里为什么用midIndex,就是因为那个被置换的小标是j是在里面的循环里面,只能先付给外面的才行
arr[midIndex] = arr[i];
//将比假设的值小的元素,付给原来假设小的元素的那个位置
arr[i] = mid;
}
}
}
三、插入排序
int[] scop = {3, 1, 5, 4, 9, 7, 6, 8};
insertSort(scop);
System.out.println("拍好后:"+ Arrays.toString(scop));
}
private static void insertSort(int[] arr) {
//循环几次,注意最后一位也要进行比较
for (int i = 1; i < arr.length; i++) {
//第一位是标志位,所以从第二位开始
int insertValue = arr[i];
//这里的意思就是,取前一位的下标,如果是从第二位开始,那就是第一位的下标也就是0
int insertIndex = i - 1;
/**
* 1、insertIndex >= 0:保证给insertValue这个数找插入的位置不越界
* 2、insertValue < arr[insertIndex]:这个意思就是标志位比前一位要小,这个时候需要让前一位向后移,继续比较标志位与前前一位,如果比他大,或者小于0了,就可以退出循环,把标志为插入到比
比较的哪位大的数据位置的后面
*
*/
while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
/**
* 假如标志位是第一位,那么从第二位开始,
* insertValue 就是第二位,
* insertIndex 就是第一位的坐标
* arr[insertIndex + 1]其实就是第二位,相当于把第二位的值再付给他自己
* 这里不能用a[i],原因是在while循环中a[i]有可能会被覆盖。 arr[insertIndex + 1] =
* arr[insertIndex];这里把前一个赋值给后一个了,两个值暂时是一样。原来的后一个的值,在
*循环前就已经拿出来放在insertvalue里面了
*/
arr[insertIndex + 1] = insertValue;
System.out.println("每一轮排序:"+ Arrays.toString(arr));
}
}
四、希尔排序
第一种:
public static void shellSort(int[] arr) {
// 循环次数控制,先将数组分成两个组进行排序,然后再对两个组进行分组,每个分组分成两组
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 利用查询排序思想
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
第二种:
public static void shellSort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
// 加了if判断
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
亲测:八千万随机数据排序
没加if:耗时28,26
加了if:耗时25,26
总的来说相差不大
五、快速排序
int[] scop = {3, 1, 5, 4, 9, 7, 6, 8};
quickSort(scop,0,scop.length-1);
System.out.println(Arrays.toString(scop));
}
private static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int mid = arr[(left + right) / 2];
int temp =0;
//比mid小的放到左边,比mid大的放到右边
while (l < r) {
//从最左边找,比中间值小的,一直知道比中间值大的就退出
while (arr[l] < mid) {
l +=1;
}
//从最右边找,找到比mid小的,就退出
while(arr[r] > mid){
r -= 1;
}
//这里说明左边已经是全部小于等于中间值,右边已经全部是大于等于中间值
if (l>= r){
break;
}
//找到了,那就要开始交换了
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
/**
* 其实就是mid这个值左右两边,刚好有两个和mid相等的数
* 然后这里只是在mid左边与mid相等时,去移动右边的下标,
* 然后右边的值和mid相等时,移动左边的下标而已
*/
//如果交换后,发现这个arr[l] == mid这个值,r--,前移
if (arr[l] == mid){
r -= 1;
}
//如果交换后,发现这个arr[r] == mid这个值,l++,后移
if (arr[r] == mid){
l += 1;
}
}
//如果l == r,必须l++,r--,否则会出现栈溢出
if(l ==r ){
l+=1;
r-=1;
}
//向左递归
if (left<r){
quickSort(arr, left, r);
}
//向右递归
if (right>l){
quickSort(arr, l, right);
}
}
六、分治算法
/**
* 归并排序
*/
public class test6 {
public static void main(String[] args) {
int[] scop = {3, 1, 5, 4, 9, 7, 6, 8};
int[] temp = new int[scop.length];
mergeSort(scop, 0, scop.length - 1, temp);
System.out.println(Arrays.toString(scop));
}
//分加合
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
/**
* 这里用了递归
* (1)第一次:执行第一个 mergeSort 执行前判断:left=0,right=7
* 左边:3, 1, 5, 4 右边:9, 7, 6, 8 这是一次循环
* 进行第二次:执行第一个 mergeSort 数组是:3, 1, 5, 4 left=0,right=3
* 左边:3, 1 右边:5, 4
* 第三次:执行第一个 mergeSort 数组是:3, 1 left=0,right=1
* 左边:3 右边 1
* 第四次前:数组是:3 left=0,right=0
* 然后left < right不满足,跳出第四次循环的,
* 执行第三次循环中第二个mergeSort,右边数组为5, 4 执行前判断:mid 为 0 ,left=mid+1 = 1,right=1 不满足left < right
* 进入merge(),数组虽然是[3, 1, 5, 4, 9, 7, 6, 8],但是他只比较,3, 1,因为这是第三次循环中的元素----3,1 之后会比较5,4(注意如果第二个mergeSort里面还有递归,还会执行第一个
* mergeSort方法)
* 如此一致递归
*
*/
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
/**
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引。比如:分成两份后,这个就是左边那份的第一个索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; //初始化i为左边有序序列的初始索引
int j = mid + 1; //初始化j为右边有序序列的初始索引
int t = 0; //这向temp数组的当前索引
/**
* 一、
* 先把左右两边有序的数组按照规则填充到temp数组中,
* 直到有一边处理完为止
*/
while (i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于右边有序序列的当前元素,就将左边的当前元素转移到temp数组中,
// 然后左边数组下边加一,temp数组下边向后移一位
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
} else {//反之将右边的放到temp当中
temp[t] = arr[j];
t += 1;
j += 1;
}
}
/**
* 二、
* 如果比完后还有剩下的,就将剩下的全部移动到temp数组当中
*/
while (i <= mid) {//左边还有剩
temp[t] = arr[i];
t += 1;
i += 1;
}
while (j <= right) {//右边还有剩
temp[t] = arr[j];
t += 1;
j += 1;
}
/**
* 三、
* 将temp数组元素拷贝到arr当中
* 注意不是每次都拷贝所有
*
* 就是合并的几个步骤
*/
t = 0;
int tempLeft = left;
//第一次合并 tempLeft = 0,right = 1 第二次:tempLeft = 2,right = 3 第三次:tempLeft = 0,right = 3
//最后一次合并:tempLeft = 0,right = 7
System.out.println("tempLeft=" + tempLeft + ",right = " + right);
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
七、基数排序
public static void sort(int[] arr) {
// 1、得到数组中最大数的位数
int max = arr[0]; // 用于接收最大数
for (int i = 1; i < arr.length; i++) {
if (arr[i] > arr[0]) {
max = arr[i];
}
}
// 1.2、获取最大数的位数
int maxLength = (max + "").length();
// 2、定义一个二维数组表示10个桶,每个桶就是一个一维数组
/**
* 2.1 二维数组包含十个桶,也就是10个一维数组
* 2.2 为防止在放入数据的时候,数据溢出,则每个一维数组的大小为数组的长度,arr.length
*/
int[][] bucket = new int[10][arr.length];
/**
* 主要是用来每个桶中放入的有效个数
* 数组初始化都是零,位置代表桶号,数组的值代表有效个数。
*/
int[] bucketElementCounts = new int[10];
// 3、开始进行数据比较,
/**
* 这里的n主要就是遍历各位数的时候,取余即可,但是遍历十位数的时候,就需要先
* 除10取整,再除10取余;比如:784 ,十位数是8,这个时候使用784/10取整得78,再对78%10 取余得到十位数8;
* 所以开始时n=1,步长为10
*/
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
// 3.1 开始对每个元素的每个位,进行排序。第一个轮回是个位,第二轮回是十位,等等。
// 下一步是循环对数组中的元素放入每个桶中;
for (int j = 0; j < arr.length; j++) {
// 取出每个元素对应位上的值
int digOfElement = arr[j] / n % 10;
/**放入到桶中,digOfElement:标志着在那个桶,
* bucketElementCounts[digOfElement]这个值第一次取出来是0,之后会自增,表示已经放入了一个有效数据
**/
bucket[digOfElement][bucketElementCounts[digOfElement]] = arr[j];
bucketElementCounts[digOfElement]++;
}
// 4、把桶里面的数据按顺序取出,放入到原数组中
int index =0;
/**
* 遍历每一个桶,并将桶中数据放入到原数组中
* <bucketElementCounts.length这个主要是取数组的有效个数,也可以使用二维数组中的10或者是数组参数arr.length
* 来代替
*index:主要是用来放入原数组时记录下标用的。
* bucketElementCounts[k]的值:为桶中的有效个数
* bucket[k][l]:桶中数据的值,可能会有多个,所以l取得是bucketElementCounts[k]
*/
for (int k = 0;k<bucketElementCounts.length;k++){
// 4.1如果该桶中有数据,再进入下一步
if (bucketElementCounts[k] != 0){
// 4.2 遍历桶中数据,bucketElementCounts[k]的值为桶中的有效个数。
for (int l = 0;l<bucketElementCounts[k];l++){
arr[index++] = bucket[k][l];
}
}
/**
* 5、最后需要清空统计有效个数的桶,为下一轮做准备
*
* 有一个问题就是:为啥没有清空二维数组,从新为下一轮做准备呢,
* 主要是:就算二维数组还有上一次排列的数组值,也不会对下一轮有影响,
* 首先是,如果下一轮出现和上一轮同样的数据,那么他会被放到同一个桶,然后
* bucket[digOfElement][bucketElementCounts[digOfElement]] = arr[j];
*digOfElement:这个相同就决定了进入同一个桶
* bucketElementCounts[digOfElement]:这个已经清空了,会从第0个位置开始放置数据,也就是说,如果
* 第0个的位置有上一轮的数据就会被覆盖。
* 最后,如果那些没有被覆盖,但是还在桶中的上一轮数据也会影响下一轮的遍历,原因就是:bucketElementCounts[digOfElement]
* 这个数组记录的有效个数已经清零,重新记录新的一轮的有效个数。取值的时候
* bucket[k][l];也都是每一个桶遍历,然后取这一轮记录下来的有效个数作为位置参数,有效个数有几个,就取几个,
* 原来的这几个数据也会被新一轮的覆盖,没有被覆盖的也取不到。
*/
bucketElementCounts[k] = 0;
}
}
}
八、二分查找
/**
* 二分查找,只能用于有序的数组当中,
*/
public class test7 {
public static void main(String[] args) {
int[] scop = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(binarySelect(scop,0,scop.length-1,5));
}
public static int binarySelect(int[] arr, int left, int right, int targ) {
//当左边大于右边时,退出递归,这是第一种情况
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
//当目标值刚好和中间值相等时,就说明找到了,退出递归
int midValue = arr[mid];
if (targ > midValue) {
//这里会一直递归,知道找到和中间值相等,或者找不到的情况下,才会退出递归
return binarySelect(arr, mid + 1, right, targ);
} else if (targ < midValue){
return binarySelect(arr, 0, mid - 1, targ);
}else {
//返回找到的中间值
return mid;
}
}
}
// 把相同的数也查找出来
public static List<Integer> search2(int[] arr, int left, int right, int finalValue){
if (left>right){
return new ArrayList<>();
}
int mid = (left+right)/2;
int midValue = arr[mid];
if (finalValue<midValue){
return search2(arr,left,mid-1,finalValue);
}else if (finalValue>midValue){
return search2(arr,mid+1,right,finalValue);
}else{
ArrayList<Integer> list = new ArrayList();
list.add(mid);
int temp = mid-1;
// 先往左边查
for (int i =mid-1;i>=0;i--){
// 由于二分查找两个相同的数是连续在一起的,所以,当连续的数不同,就说明数据不相等了
if (arr[i]!= finalValue){
break;
}
list.add(i);
}
temp = mid+1;
// 再往右边查
for (int j = mid+1;j<arr.length;j++){
if (arr[j] != finalValue){
break;
}
list.add(j);
}
return list;
}
}