算法


第一部分 排序算法

  1. 直接插入排序

    //基本思想:
    //每次将待排序的元素插入到前面已经排序好的序列中
    public void insertSort(int[] nums){
        int n=nums.length;
        for(int i=1;i<n;i++){
            if(nums[i]>=nums[i-1])continue;//若后一个元素大于前一个元素,则此元素不必排序
            else{
                int temp=nums[i];
                int j=i-1
                for(;nums[j]>temp;j--)nums[j+1]=nums[j];//进行移动
                nums[j+1]=temp;
            }
        }
    }
    
  2. 折半插入排序

    //基本思想:
    //每次将待排序的元素插入到前面已经排序好的序列中,查找插入位置时,使用二分查找
    public void binaryInsertSort(int[] nums){
        int n=nums.length;
        for(int i=1;i<n;i++){
            if(nums[i]>=nums[i-1])continue;//若后一个元素大于前一个元素,则此元素不必排序
            else{
                int temp=nums[i];
                
                int low=0,high=i-1;//折半查找,最后位置是high+1
                while(low<=high){
                    int mid=(low+high)/2;
                    if(nums[mid]>temp)high=mid=-1;
                    else low=mid+1;
                }
                
                int j=i-1
                for(;j>=high+1;j--)nums[j+1]=nums[j];//进行移动,注意high+1
                nums[high+1]=temp;
            }
        }
    }
    
    
  3. 希尔排序

    //基本思想:
    //使用间隔将数组分为多个子数组,每个子数组使用直接插入排序,最后全体使用直接插入排序
    public void shellSort(int[] nums){
        int n=nums.length;
        for(int k=n/2;k>=1;k=k/2){
            for(int i=k+1;i<n;i++){
                if(nums[i]>=nums[i-k])continue;//若后一个元素大于前一个元素,则此元素不必排序
                else{
                    int temp=nums[i];
    
                    int j=i-k
                    for(;j>0&&nums[j]>temp;j-=k)nums[j+k]=nums[j];//进行移动,每次间隔k
                    nums[j+k]=temp;
                }
            }
        }
    }
    
  4. 冒泡排序

    //基本思想:
    //从后往前比较元素的值,每次将最小的值冒泡到数组前端
    //或者:从前往后比较,每次排序较大的值
    public void bubbleSort(int[] nums){
        int n=nums.length;
        boolean flag=false;//若某一趟未交换,则说明已经无需排序
        for(int k=0;k<n-1;k++){//冒泡n-1趟
            for(int i=n-1;i>k;i--){
                if(nums[i]<nums[i-1]){
                    swap(nums[i],nums[i-1]);
                    flag=true;
                }
            }
            if(flag==false)return;
        }
    
    }
    
  5. 快速排序

    //基本思想:
    //每次选取一个枢轴,交换使得该元素左边元素比他小,右边元素比他大,该元素在最终位置上
    //分治的思想
    public void quickSort(int[] nums){
        int n=nums.length;
        int low=0,high=n-1;
        int pivot=patition(nums,low,high);
        quikSort(nums,low,pivot-1);
        quikSort(nums,pivot+1,high);
    }
    
    public int patition(int[] nums,int low,int high){
        int pivot=nums[low];
        while(low<high){
            while(low<high&&nums[high]>pivot)high--;
            nums[low]=nums[high];
            while(low<high&&nums[low]<pivot)low--;
            nums[high]=nums[low];
        }
        nums[low]=pivot;//最后位置,最后low和high重合
        return low;
    }
    
  6. 简单选择排序

    //基本思想:
    //每次选择剩余序列中最小的元素,插入到依次的位置
    public void simpleSelectSort(int[] nums){
        int n=nums.length;
        int min=0;
        for(int i=0;i<n-1;i++){
            min=i;
            for(int j=i+1;j<n;j++){
                if(nums[j]<nums[min])min=j;
            }
            if(i!=min)swap(nums[i],nums[min]);
        }
    }
    
  7. 堆排序(未理解)

    //基本思想:
    //1.构造初始堆
    //2.输出堆顶元素->堆底送入堆顶->调整
    
    //构造:建立大根堆
    public void buildMaxHeap(int[] nums,int len){
        for(int i=len/2;i>0;i--){
            headAdjust(nums,i,len);
        }
    }
    
    //调整
    public void headAdjust(int[] nums,int k,int len){
        int temp=nums[k];
        for(int i=2*k;i<=len;i*=2){
            if(i<len&&nums[i]<nums[i+1])i++;//选择key较大子结点
            if(temp>=nums[i])break;
            else{
                nums[k]=nums[i];//较大上浮
                k=i;//修改k值,继续向下筛选(上层修改会影响下层)
            }
        }
        nums[k]=temp;//放入最终位置
    }
    
    
    //堆排序
    public void heapSort(int[] nums){
        int len=nums.length;
        buildMaxHeap(nums,len);
        for(int i=len-1;i>0;i--){//n-1趟交换与建堆
            swap(nums[i],nums[0]);//输出堆顶元素
            headAdjust(nums,i,i-1);
        }
    }
    
  8. 归并排序

    //基本思想:
    //合并操作(A[low...mid]、A[mid+1...high]):先复制到辅助数组B->分别从B中每段取一个比较大小->较小放入A->当B中某一段越界,将另一段全部复制到A中
    
    private int[] b=new int[nums.length];
    
    //合并操作
    public void merge(int[] nums,int low,int mid,int high){
        int i=0,j=0,k=0;
        for(k=low,k<=high;k++)b[k]=nums[k];
        for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
            if(b[i]<=b[j])nums[k]=b[i++];
            else nums[k]=b[j++];
        }
        
        while(i<=mid)nums[k++]=b[i++];
        while(j<=high)nums[k++]=b[j++];
    }
    
    
    //归并排序
    public void mergeSort(int[] nums,int low,int high){
        if(low<high){
            int mid=(low+high)/2;
            mergeSort(nums,low,mid);
            mergeSort(nums,mid+1,high);
            merge(nums,low,mid,high);
        }
    }
    
  9. 基数排序

    //基本思想:
    //基于关键字各位的大小进行排序
    //分配+收集
    

第二部分 查找算法

  1. 二分查找

    public int search(int[] arr,int target){
            int begin =0;
            int end=arr.length-1;
            while(begin<=end)
            {
                int mid=(begin+end)/2;
                if(arr[mid]==target) return mid;
                if(arr[mid]>target) end=mid-1;
                else begin=mid+1;
            }
            return -1;
        }
    }
    

第三部分 遍历算法

一、树的遍历

  1. 先序遍历

    //递归
    public void preOrder(Node root){
        if(root==null)return;
        visit(root);
        preOrder(root.left);
        preOrder(root.right);
    }
    
    //迭代---使用栈
    public void preOrder(Node root){
        if(root==null)return;
        Stack<Node> stack=new Stack<>();
        
        Node p=root;
        while(p!=null||!stack.empty()){
            if(p!=null){
                visit(p);
                stack.push(p);
                p=p.left;
            }
            else{
                p=stack.pop();
                p=p.right;
            }
        }
    }
    
  2. 中序遍历

    //递归
    public void inOrder(Node root){
        if(root==null)return;
        preOrder(root.left);
        visit(root);
        preOrder(root.right);
    }
    
    //迭代---使用栈
    public void inOrder(Node root){
        if(root==null)return;
        Stack<Node> stack=new Stack<>();
        
        Node p=root;
        while(p!=null||!stack.empty()){
            if(p!=null){
                stack.push(p);
                p=p.left;
            }
            else{
                p=stack.pop();
                visit(p);
                p=p.right;
            }
        }
    }
    
  3. 后序遍历

    //递归
    public void postOrder(Node root){
        if(root==null)return;
        preOrder(root.left);
        preOrder(root.right);
        visit(root);
    }
    
    //迭代---使用栈+记录是否遍历又指针
    public void postOrder(Node root){
        if(root==null)return;
        Stack<Node> stack=new Stack<>();
        
        Node p=root;
        Node r=null;
        while(p!=null||!stack.empty()){
            if(p!=null){
                stack.push(p);
                p=p.left;
            }
            else{
                p=stack.peek();
                if(p.right!=null&&p.right!=r){//判断右子树是否已经被访问
                    p=p.right;
                }
                else{
                    p=stack.pop();
                    visit(p);
                    r=p;
                    p=null;
                }
            }
        }
    }
    
  4. 层次遍历:队列

    //使用队列
    public void levelOrder(Node root){
        if(root==null)return;
        
        Queue<Node> queue=new LinkedList<>();
        queue.offer(root);
        
        while(!queue.isEmpty()){
            int size=queue.size();
            Node p=null;
            
            for(int i=0;i<size;i++){
                p=queue.poll();
                visit(p);
                if(p.left!=null)queue.offer(p.left);
                if(p.right!=null)queue.offer(p.right);
            }
        }
    }
    

二、图的遍历

  1. 深度优先算法:递归实现

    public void dfs(boolean[] isVisited,int i){
        //把当前结点的 值设置成true
        isVisited[i]=true;
        //获取当前结点的第一个邻接点
        int w = getFirstNeighbour(i);
        while (w!=-1){
            if (!isVisited[w]){
                dfs(isVisited, w);
            }
            //获取当前结点的下一个结点
            w = getNextNeighbour(i, w);
        }
    }
    
    //返回当前结点的第一个临界结点
    public int getFirstNeighbour(int index){
        for (int i=0;i<vertexList.size();i++){
            if (edges[index][i]>0){
                return i;
            }
        }
        return -1;
    }
    
    //返回当前结点的下一个结点
    public int getNextNeighbour(int v1,int v2){
        for (int j=v2+1;j<vertexList.size();j++){
            if (edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }
    
    //访问邻居节点相当于遍历邻接矩阵的一行
    
  2. 广度优先算法:队列实现

    public void bfs(boolean[] isVisited,int i){
        int u;//素表示队列头的第一个元素
        int w;
        //用一个队列来进行保存访问过的
        LinkedList<Integer> queue = new LinkedList<>();
        //把当前结点标记为以访问
        isVisited[i]=true;
        //将该结点加入到队列中
        queue.addLast(i);
    
        //如果队列不为空
        while (!queue.isEmpty()) {
            //获取队列头部的第一个结点
            u=queue.removeFirst();
            //获取当前结点的第一个邻接点
            w = getFirstNeighbour(u);
            while (w != -1) {
                //判断该结点是否访问过
                if (!isVisited[w]) {
                    //如果没有访问过,就输出(这里是和深度优先搜索遍历不一样的地方)
                    isVisited[w] = true;
                    queue.addLast(w);
                }
                w = getNextNeighbour(u, w);
            }
        }
    }
    
  3. 参考链接

第四部分 Hash算法

一、解决hash冲突

二、md4,md5,sha-hash

  1. MD5算法
    • 概念:MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆
    • 实现原理:
      1. 数据填充:填充消息使其长度与448模512同余(长度=448 mod 512),即时消息长度本身已经满足了上述长度要求也需要填充。填充方法:附一个1在消息后面,然后用0填充,直至符合长度要求(长度=448 mod 512)。
      2. 添加长度:在上一步结果之后添加64位的消息长度。如果消息长度大于264,则只使用其低64位。最终消息长度正好是512的整数倍。
      3. 初始化变量:用4个变量(A、B、C、D)计算消息摘要。A、B、C、D都是32位寄存器。这些寄存器以下面的16进制数来初始化:A=01234567h ,B=89abcdefh , C=fedcba98h , D=76543210h
      4. 数据处理:以512位分组为单位,进行处理消息。首先定义4个辅助函数,每个都是以3个32位双字作为输入,输出1个32位双字。4个函数:
            F(X,Y,Z)=(X & Y) | ((~X) & Z);
            G(X,Y,Z)=(X & Z) | (Y & (~Z));
            H(X,Y,Z)=X ^ Y ^ Z;
            I(X,Y,Z)=Y ^ (X | (~Z));
        "&“是与操作,”|“是或操作,”~“是非操作,”^"是异或操作。
      5. 这4轮变换是对进入主循环的512位消息分组的16个32位字分别进行如下操作:将A、B、C、D的副本a、b、c、d中的3个经F、G、H、I运算后的结果与第4个相加,再加上32位字和一个32位字的加法常数,并将所得之值循环左移若干位,最后将所得结果加上a、b、c、d之一,并回送至ABCD,由此完成一次循环。
      6. 最后的输出是A,B,C和D的级联。最后得到的A,B,C,D就是输出结果,A是低位,D为高位,DCBA组成128位输出结果。
    • 参考链接
      1. https://blog.csdn.net/Hotspurs/article/details/89202585?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-4.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-4.pc_relevant_default&utm_relevant_index=8
      2. https://www.cnblogs.com/sthu/p/9981328.html
posted @ 2021-12-22 15:43  汤十五  阅读(34)  评论(0)    收藏  举报