算法
第一部分 排序算法
-
直接插入排序
//基本思想: //每次将待排序的元素插入到前面已经排序好的序列中 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; } } } -
折半插入排序
//基本思想: //每次将待排序的元素插入到前面已经排序好的序列中,查找插入位置时,使用二分查找 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; } } } -
希尔排序
//基本思想: //使用间隔将数组分为多个子数组,每个子数组使用直接插入排序,最后全体使用直接插入排序 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; } } } } -
冒泡排序
//基本思想: //从后往前比较元素的值,每次将最小的值冒泡到数组前端 //或者:从前往后比较,每次排序较大的值 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; } } -
快速排序
//基本思想: //每次选取一个枢轴,交换使得该元素左边元素比他小,右边元素比他大,该元素在最终位置上 //分治的思想 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; } -
简单选择排序
//基本思想: //每次选择剩余序列中最小的元素,插入到依次的位置 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]); } } -
堆排序(未理解)
//基本思想: //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); } } -
归并排序
//基本思想: //合并操作(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); } } -
基数排序
//基本思想: //基于关键字各位的大小进行排序 //分配+收集
第二部分 查找算法
-
二分查找
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; } }
第三部分 遍历算法
一、树的遍历
-
先序遍历
//递归 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; } } } -
中序遍历
//递归 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; } } } -
后序遍历
//递归 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; } } } } -
层次遍历:队列
//使用队列 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); } } }
二、图的遍历
-
深度优先算法:递归实现
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; } //访问邻居节点相当于遍历邻接矩阵的一行 -
广度优先算法:队列实现
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); } } } -
参考链接
第四部分 Hash算法
一、解决hash冲突
二、md4,md5,sha-hash
- MD5算法
- 概念:MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆
- 实现原理:
- 数据填充:填充消息使其长度与448模512同余(长度=448 mod 512),即时消息长度本身已经满足了上述长度要求也需要填充。填充方法:附一个1在消息后面,然后用0填充,直至符合长度要求(长度=448 mod 512)。
- 添加长度:在上一步结果之后添加64位的消息长度。如果消息长度大于264,则只使用其低64位。最终消息长度正好是512的整数倍。
- 初始化变量:用4个变量(A、B、C、D)计算消息摘要。A、B、C、D都是32位寄存器。这些寄存器以下面的16进制数来初始化:A=01234567h ,B=89abcdefh , C=fedcba98h , D=76543210h
- 数据处理:以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));
"&“是与操作,”|“是或操作,”~“是非操作,”^"是异或操作。 - 这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,由此完成一次循环。
- 最后的输出是A,B,C和D的级联。最后得到的A,B,C,D就是输出结果,A是低位,D为高位,DCBA组成128位输出结果。
- 参考链接
- 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
- https://www.cnblogs.com/sthu/p/9981328.html

浙公网安备 33010602011771号