java几大算法代码与优化

public interface ISort {
	//常规实现
    void sort(int[] sources);
    //有优化版的填写优化版
    void sortOpt(int[] sources);
}

冒泡

冒泡排序。
时间复杂度O(n^2)
空间复杂度O(1)
稳定排序
原地
简单
交换次数O(n^2)
普通版和优化版

算法优点:稳定
算法缺点:所需时间太长,时间复杂度高,接近O(n²)
冒泡排序理论上总共要进行n(n-1)/2次交换
仅供学习,不适合正式使用

public final class BubbleSort implements ISort {
    @Override
    public void sort(int[] sources) {
        int size = sources.length;
        while (size-- > 0) { //1. 外层循环不断缩小 总个数
            for (int i = 0; i < size; i++) { //内循环从 0 < size 这个不断减小的数
                if (sources[i] > sources[i + 1]) { //交换
                    int tmp = sources[i];
                    sources[i] = sources[i + 1];
                    sources[i + 1] = tmp;
                }
            }
        }
    }

    @Override
    public void sortOpt(int[] sources) {
        int size = sources.length;
        //追加一个标记上次排序已经好了的位置
        int mark = 0;
        while (size-- > 0) {
            boolean isNoReplace = true; //1. 通过内循环是否有交换动作,来跳出#1
            for (int i = mark; i < size; i++) { //2. 标记已经排好了的位置,用于下次直接从那个位置开始即可 #2
                if (sources[i] > sources[i + 1]) {
                    if (isNoReplace) { //1. 通过内循环是否有交换动作,来跳出#2
                        isNoReplace = false;
                        mark = i; //2. 标记已经排好了的位置,用于下次直接从那个位置开始即可 #1
                    }
                    int tmp = sources[i];
                    sources[i] = sources[i + 1];
                    sources[i + 1] = tmp;
                }
            }
            if (isNoReplace) { //1. 通过内循环是否有交换动作,来跳出#3
                break;
            }
        }
    }
}

选择排序

选择排序。
时间复杂度O(n^2)
空间复杂度O(1)
不稳定排序
原地
简单
交换次数O(n)
算法来讲,因为数据交换最小的,大部分情况优于冒泡。
优化算法为一次找到最大和最小。

算法优点:移动数据的次数已知。
算法缺点:比较次数多;不稳定。
选择排序总共要进行n-1次交换

public final class SelectionSort implements ISort {
    @Override
    public void sort(int[] sources) {
        int size = sources.length;

        for(int i = 0; i < size - 1; i++) {
            int minIndex = i;
            //1.1 一趟之后,找到了min index
            for (int j = i + 1; j < size; j++) {
                if (sources[j] < sources[minIndex]) {
                    minIndex = j;
                }
            }
            //1.2 把miniIndex放到前面来
            if (minIndex != i) {
                int tmp = sources[i];
                sources[i] = sources[minIndex];
                sources[minIndex] = tmp;
            }
        }
    }

    @Override
    public void sortOpt(int[] sources) {
        int size = sources.length;

        for(int left = 0, right = size - 1; left < right; left++, right--) {
            int minIndex = left;
            int maxIndex = right;
            //1.1 一趟之后,找到了min index & max index
            for (int j = left; j <= right; j++) {
                if (sources[j] < sources[minIndex]) {
                    minIndex = j;
                }

                if (sources[j] > sources[maxIndex]) {
                    maxIndex = j;
                }
            }
            //1.2 把miniIndex放到前面来
            if (minIndex != left) {
                int tmp = sources[minIndex];
                sources[minIndex] = sources[left];
                sources[left] = tmp;

                //因为上面这个交换动作,会把left和minIndex交换位置。
                // 所以,如果我们的最大值刚好就是这个left,那已经被我们交换走了。所以得换到minIndex的位置
                if (left == maxIndex) {
                    maxIndex = minIndex;
                }
            }
                //1.3 把maxIndex放到后面去
            if (maxIndex != right) {
                int tmp = sources[maxIndex];
                sources[maxIndex] = sources[right];
                sources[right] = tmp;
            }
        }
    }
}

插入排序

插入排序。
时间复杂度O(n^2) 但对于比较有序的数列,时间复杂度能大幅下降
空间复杂度O(1)
稳定排序
原地
简单
交换次数O(n^2)
算法优点:稳定,快。
算法缺点:比较次数不一定,比较次数越多,插入点后的数据移动越多(特别是当数据总量庞大的时候)。但用链表可以解决这个问题。
最好情况:序列已经是期望顺序了,在这种情况下,需要进行的比较操作需(n-1)次即可。
最坏情况:序列是期望顺序的相反序列,那么此时需要进行的比较共有n(n-1)/2次。
插入排序算法的时间复杂度平均为O(n^2)


public final class InsertSort implements ISort {
    @Override
    public void sort(int[] sources) {
        int N = sources.length;
        //从第2,i=1个元素开始。
        for (int i = 1; i < N; i++) {
            int cur = sources[i];
            //从i-1开始,倒着跟前面的数比较
            int r = i;
            while (r > 0 && cur < sources[r - 1]) {
                sources[r] = sources[r - 1];
                r--; //关键是这个要放里面去
            }
            //如果r == i表示while里面没有出现小于的情况,则跳出了
            if (r != i) {
                sources[r] = cur;
            }
        }
    }

    @Override
    public void sortOpt(int[] sources) {

    }
}

shell排序

希尔排序。 插入排序的优化版。
时间复杂度O(n^(1.3-2))
空间复杂度O(1)
不稳定排序
原地
复杂
交换次数
时间复杂度:需要根据增量的选择来看,通常比O(n^2)要好
优势:最坏情况下和平均情况差不多,中小规模输入
劣势:大规模输入(比一般的O(n^2)要好) 不稳定
,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法.
本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。
原因是,当n值很大时数据项每一趟排序需要移动的个数很少,但数据项的距离很长。当n值减小时每一趟需要移动的数据增多,
此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。

public class ShellSort implements ISort {

    @Override
    public void sort(int[] sources) {
        int size = sources.length;

        int gap = Math.max(1, size  * 2 / 5); //gap任取
        //不要求面试能直接写出来。
        //关键点就是三层循环。
        //最外层循环:gap的计算逻辑;
        //第二层循环:从gap开始,到size的循环。
        //第三层循环:根据步调gap,进行插入排序的一趟。

        while(true) {
            for (int i = gap; i < size; i++) {
                int tmp = sources[i];
                int r = i;
                while (r -gap >= 0 && tmp < sources[r - gap]) {
                    sources[r] = sources[r - gap];
                    r -= gap;
                }

                sources[r] = tmp;
            }

            if (gap == 1) {
                break;
            }
            //计算gap
            gap = gap > 6 ? gap / 3 : 1;
        }
    }

    @Override
    public void sortOpt(int[] sources) {
        throw new RuntimeException("no");
    }
}

快速排序

快速排序。 迭代版本。
时间复杂度O(nlogn)
空间复杂度O(log(n)) 体现在函数栈
不稳定排序
原地
普通版和优化版

算法优点:稳定
一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),
且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。
所以,对稳定性没有要求的情况下,快速排序总是优于归并排序。


public final class QuickSort implements ISort {
    @Override
    public void sort(int[] sources) {
        quickSort(sources, 0, sources.length - 1);
    }

    private void quickSort(int[] src, int left, int right) {
        if (left > right) {
            return;
        }

        int mark = partition(src, left, right);
        quickSort(src, left, mark - 1);
        quickSort(src, mark + 1, right);
    }

    private int partition(int[] src, int left, int right) {
        int pivotVal = src[left];

        int l = left; //记录左右指针
        int r = right;

        while (l < r) {
            //因为起手坑在pivot的位置,故而先从右边开始。
            // 如果右边小于基准值,把src[r]变成'坑', 交换到l的位置。l++,r不变。因为他是'坑'.
            while (src[r] > pivotVal && l < r) {
                r--;
            }
            if (l < r) {
                src[l++] = src[r];
            }

            while(src[l] < pivotVal && l < r) {
                l++;
            }
            if (l < r) {
                src[r--] = src[l];
            }
        }
        src[l] = pivotVal;
        return l;
    }

    @Override
    public void sortOpt(int[] sources) {

    }
}

归并排序

时间复杂度O(nlogn)
空间复杂度O(n)
稳定排序
并非原地

算法优点:稳定
当有 n 个记录时,需进行 logn 轮归并排序,每一轮归并,其比较次数不超过 n,元素移动次数都是 n,因此,归并排序的时间复杂度为 O(nlogn)。
归并排序时需要和待排序记录个数相等的存储空间,所以空间复杂度为 O(n)。
归并排序适用于数据量大,并且对稳定性有要求的场景。

他适合超大型数据的,使用外部磁盘存储排序的方案。
改进点还有多路归并,现在是二叉归并。

//归并排序 递归版本
public final class MergeSortRecursive implements ISort {
    private int[] src;
    private int[] tmp;

    @Override
    public void sort(int[] sources) {
        src = sources;
        tmp = new int[src.length];
        sort(0, sources.length - 1);
    }

    private void sort(int left, int right) {
        //优化点2:当数组比较小时,可以使用插入排序,效率更高。
        //if(right-left+1<16) insertSort(int[] arr,int left,int right);

        if (left >= right) { //停止递归条件
            return;
        }
        //divide 分
        int mid = (left + right) >> 1;
        sort(left, mid);
        sort(mid + 1, right);
        //conquer 合
        //优化点1:若已经有序,则不需要归并。更适用于近乎有序的数组。
        if (src[mid] > src[mid + 1]) {
            merge(src, tmp, left, mid, right);
        }
    }

    private static void merge(int[] src, int[] tmp, int left, int mid, int right) {
        //记录下,我们需要merge的2个区间;
        // 左边区间[left, mid];右边区间从[mid+1,right]
        //t记录下我们塞入tmp数组的index
        int l = left;
        int r = mid + 1;
        int t = 0;

        //拷贝到一个临时数组中去;其实优化点当数据比较少的时候,可以针对这里进行插入排序;不使用tmp数组
        //不断的从2边取放到tmp中,直到有一遍触底
        while (l <= mid && r <= right) {
            if (src[l] <= src[r]) {
                tmp[t++] = src[l++];
            } else {
                tmp[t++] = src[r++];
            }
        }

        //将剩余的一边仅需考入到tmp中
        while (l <= mid) {
            tmp[t++] = src[l++];
        }

        while (r <= right) {
            tmp[t++] = src[r++];
        }


        //将tmp还原到src中
        while (--t >= 0) { //此时tempIndex其实是前面我们存入的tmpSize。所以要先--
            src[right--] = tmp[t];
        }
    }

    @Override
    public void sortOpt(int[] sources) {

    }
}
/**
 * 归并排序。 迭代版本
 */
public final class MergeSortIterative implements ISort {
    private int[] src;
    private int[] tmp;

    @Override
    public void sort(int[] sources) {
        src = sources;
        int size = src.length;
        tmp = new int[size];

        //外层循环从1开始;逐渐扩大??
        for (int step = 1; step < size; step *= 2) {
            int doubles = 2 * step;
            //从底部开始往上合并
            for (int start = 0; start < size; start += doubles) {
                int mid = Math.min(size - 1, start + step);
                int right = Math.min(size - 1, start + doubles - 1);
                merge(src, tmp, start, mid, right);
            }
        }
    }

    private static void merge(int[] src, int[] tmp, int left, int mid, int right) {
        //记录下,我们需要merge的2个区间;
        // 左边区间[left, mid];右边区间从[mid+1,right]
        //t记录下我们塞入tmp数组的index
        int l = left;
        int r = mid + 1;
        int t = 0;

        //拷贝到一个临时数组中去;其实优化点当数据比较少的时候,可以针对这里进行插入排序;不使用tmp数组
        //不断的从2边取放到tmp中,直到有一遍触底
        while (l <= mid && r <= right) {
            if (src[l] <= src[r]) {
                tmp[t++] = src[l++];
            } else {
                tmp[t++] = src[r++];
            }
        }

        //将剩余的一边仅需考入到tmp中
        while (l <= mid) {
            tmp[t++] = src[l++];
        }

        while (r <= right) {
            tmp[t++] = src[r++];
        }


        //将tmp还原到src中
        while (--t >= 0) { //此时tempIndex其实是前面我们存入的tmpSize。所以要先--
            src[right--] = tmp[t];
        }
    }

    @Override
    public void sortOpt(int[] sources) {

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。
Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值