算法专栏
niceYF
这个作者很懒,什么都没留下…
展开
-
哈希函数的简单算法题
已知:无符号整数的范围0~2的32次方-1,现在有四十亿个无符号整数,求其中出现次数最多的那个数。可以使用的内存不超过一个G#经典解法:设置一个map(Integer,Integer),key值存数,value值存出现的次数。map中存储一个值至少需要4+4B=8B,map本身的索引什么的还会需要空间。40亿乘8B= 320亿B 差不多等于32G的空间,远远的超过1个G的空间。哈希解法把每个数求出一个哈希值,哈希值再模上100,把得到相同的余数的放到一个文件里面。每个文件大小是32G/100原创 2022-04-03 00:25:48 · 385 阅读 · 0 评论 -
什么是哈希函数
Hash函数又称为散列函数。是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出的值称为散列值或者消息摘要。其实就是你随便给我一个输入,我返回你一个固定长度的消息摘要。常见Hash函数的实现:1.MD5是一种被广泛使用的密码散列函数。MD5可以产生出一个128位(16个字节)的散列值。特点:压缩性:任意长度的数据,运算得出的MD5值长度都是固定的(128位)。容易计算:很容易从原数据计算出对应的MD5值。抗修改性:对原始数据的改动,得到的MD5值有很大的区别。强抗碰撞:已知原原创 2022-04-02 21:34:45 · 1406 阅读 · 0 评论 -
暴力递归--袋子重量问题
思想还是差不多,就从左往右依次去尝试,选和不选两种情况。public static int MaxValue(int[] values,int[] weights,int bag) { return process(values,weights,0,0,bag); } public static int process(int[] values,int[] weights,int i,int alreadyWeight,int bag) { if(alreadyWeight==bag..原创 2022-03-20 16:22:00 · 127 阅读 · 0 评论 -
暴力递归--数字转换
当指向字符串中的一个字符的时候,转化的可能有两个1.自己一个字符成一个字母2.和下一个字符成一个字母(得两个数加起来小于等于26)注意:当指向字符‘0’的时候就肯定这条路走不通了,因为没有01,02,03这样的转换。 public static int numberToString2(String str) { char[] chs = str.toCharArray(); return process2(chs,0); } public static int pro..原创 2022-03-19 21:32:39 · 132 阅读 · 0 评论 -
暴力递归--逆序栈
先将一个栈中的最低元素抽取出来,其他元素往下移动一位。//f函数负责将栈中最低下的一个数抽取出来,其他保持不变。 public static int f(Stack<Integer> stack) { int result = stack.pop(); if(stack.isEmpty()) {//如果空了,就说明弹出的元素是最后一个了,直接返回 return result; }else {//如果不是就继续循环 int last = f(stack);//再..原创 2022-03-19 20:21:53 · 125 阅读 · 0 评论 -
暴力枚举--纸牌问题
递归的思想:如果A走先手,它有两种选择拿左边或者右边。A获得的最大值就是第一次拿左边最大值,和第一次拿右边的最大值进行比较,选择其中较大的一个。base case就是L == R 的时候,直接返回arr[L]就行。第二次就是后手拿了,因为对方也是聪明绝顶,所以后手拿拿到的肯定是较小的。真的很神奇感觉public static int win1(int[] arr) { return Math.max(f(arr,0,arr.length-1), s(arr,0,arr.原创 2022-03-19 19:44:02 · 156 阅读 · 0 评论 -
暴力递归--全排列(去重)和求子串
全排列:我们可以认为往位置上放数字,第一位上所有的数字都可以放,第二位上除了第一位的数所有的都可以放。每次都是固定一个数,然后求后面的全排列。 //全排列。 public static ArrayList<String> Permutation(String str){ if(str == null || str.length() == 0) { return null; } char[] chs = str.toCharArray(); ArrayList<原创 2022-03-19 18:15:54 · 268 阅读 · 0 评论 -
暴力递归--汉诺塔问题
该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置N个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。我们需要打印这个移动的顺序。我们将三个杆子分别称为,start,end,other,完成这个,我们只需要三步:将N-1层的盘子通过end移动到other上:将N层的盘子从start移动到end上:将原创 2022-03-19 14:47:40 · 3185 阅读 · 0 评论 -
dijkstra算法的改进--堆
dijksta每次都需要从所有的距离最小的点中选择一个进行扩展。而进行选择距离最下的点的时候使用的是遍历,每次遍历找出最下的那个点。这样的效率肯定是不高的。我们可以使用一个小根堆结构,将所有的节点放入小根堆中,然后每次取出堆顶元素即可。每次更新的时候需要将小根堆中的元素的值进行更改。这显然无法使用系统提供的堆结构。所以我们只有手动写堆结构来实现。 //弹出的节点和距离信息 public static class NodeRecord { public Node node; public原创 2022-03-19 14:05:51 · 930 阅读 · 0 评论 -
N皇后问题的常数优化
每一行放一个皇后,那么就不存在行上的限制。只存在列上的限制,左斜线的限制和右斜线的限制。我们可以分别用三个数来代表列上的限制,左斜线的限制和右斜线的限制。假如在八皇后中第一行放的是那么第二行列上的限制就是左斜线的限制就是右斜线的限制就是三个限制或得到整个的限制也就是说第二行除了这几个地方不可以放,其他都可以其他行也是一样的处理。public static int num2(int n) { if(n<1 || n>32) {..原创 2022-03-19 12:31:54 · 204 阅读 · 0 评论 -
深度遍历--N皇后问题
一个N乘N的棋盘中,放入N颗棋子,这些棋子不可以同行同列同斜线。求有多少种放发。思路:每一行放一个,就可以保证不同行,然后只要判断是否同列和同斜线即可。同斜线的斜率为1。运用深度遍历的思想去做这个题,public static int num1(int n) { if(n<1) { return 0; } int[] recode = new int[n]; return process1(0,recode,n); } public static int原创 2022-03-18 16:13:22 · 267 阅读 · 0 评论 -
深度遍历--N皇后问题
一个N乘N的棋盘中,放入N颗棋子,这些棋子不可以同行同列同斜线。求有多少种放发。思路:每一行放一个,就可以保证不同行,然后只要判断是否同列和同斜线即可。同斜线的斜率为1。运用深度遍历的思想去做这个题,public static int num1(int n) { if(n<1) { return 0; } int[] recode = new int[n]; return process1(0,recode,n); } public static int原创 2022-03-18 16:13:22 · 267 阅读 · 0 评论 -
堆排序--数据流中,随时获得中位数。
要拿到中位数就需要得到中间的一个或者两个数。思路:把数据分成左右两部分,左右两边的数据量不超过1,左边小于右边,左边拿到最大值,右边拿到最小值,这两个数肯定可以得到中位数。那么左边可以用大根堆,右边用小根堆。如果在有数进来的时候如果小于大根堆的堆顶,就进入大根堆,如果大于等于大根堆的堆顶,就进入小根堆。当加入数据两个堆的大小超过1的时候,数据大的那个堆弹出堆顶元素到另外一个堆中。 //大根堆比较器 public static class bigComparator implements Co原创 2022-03-17 22:15:45 · 584 阅读 · 0 评论 -
贪心算法--项目花费与收益问题
思路:将所有项目按照花费从小到大排序,选出所有的可以做的项目,然后找一个收益最大的。现在资金增多了,继续选出所有的可以做的项目,再找一个收益最大的。直到k等于0;实现:将输入两个数组中的每个位置的花费和收益两个值结合成一个节点。按照花费从小到大放入小根堆,如果花费小于现有资金就放入一个利益从大到小排序的大根堆中。然后从大根堆中弹出一个最大利益的值。现有资金增加,然后循环直到k等于0;public static class Node{//项目 int profit; int co..原创 2022-03-17 21:14:19 · 410 阅读 · 0 评论 -
算法--前缀树
前缀树上面的每一个节点,都可以有很多个子树,通往不同子树的路径上的字符串也不同。通常用于保存字符串,以及查询字符串,通过前缀相同来减少查询时间。前缀树首先要有一个根节点。假设我需要存储三个字符串分别是"abc" "abd" "bcd"。开始的时候根节点没有通向a的路径于是根节点创建出一条通向'a'的路径到达下一个节点之后发现没有通向‘b’的路径于是再创建路径。后面是一样的最后就是一个这样的图。每个节点中包含一个集合,集合中表示它可以通向的下一个节点,也就是路径。如果增加字符.原创 2022-03-17 15:13:22 · 759 阅读 · 0 评论 -
图的Dijkstra算法
Dijkstra算的是一个点到图中所有点的最小距离。思路:假设我们需要求的是到点A的距离。我们需要一张距离表,表示A点到其他点的距离,如果这张表中没有一个点,那就表示A点到那个表中的距离为无穷大。每次选取一个距离A最小的点进入,通过这个距离最小的点,可以使得距离表中的某些数据发生更新。然后继续选择一个距离最小的点,选择过的点是不可以重复再选的。 public static Map<Node,Integer> dijkstra(Node node){ //距离表 HashMa原创 2022-03-16 18:18:52 · 345 阅读 · 0 评论 -
图的最小生成树prim算法
prim算法:先从图中随机选取一个点,从它的边中选择一个最小的进行扩展。扩展完之后这两个点变成一个集合,他们的边集也会进行相应的扩展,再从扩展的边集中找一个最小的边进行扩展。扩展的时候要注意,扩展的边一定不能是集合内部的,就不能是边的to节点是集合内部的。一直循环,直到所有的点都扩展完了就行了。public static Set<Edge> primMST(Graph graph){ PriorityQueue<Edge> priority = new Pr原创 2022-03-16 16:59:04 · 421 阅读 · 0 评论 -
图的最小生成树Kruskal算法
最小生成树其实就是最小权重生成树的简称。在一个图里面有很多条边,每条边都有权重。使得每个节点都可以相互联通且整体的权重最小的边的集合就是最小生成树。思路:假设每个节点都有一个集合,这个集合就是这个节点联通的节点。在开始没有边的时候,这个集合内只有自己。把边按照权重从小到大的顺序进行排列。每次拿出一条边(权重最小的)加入到图中。如果边的 from节点的联通集合和 to节点的联通集合不相等的话,那么这条边就是可以用的。然后把to节点联通集合内的所有节点加入到from节点中,然后让to原创 2022-03-16 16:27:37 · 425 阅读 · 0 评论 -
图的拓扑排序
什么是拓扑排序?在一个工程项目中,所做的事情肯定是有先后的,比如盖楼的时候得先打地基,然后砌砖,最后才可以刷墙。那么顺序就是打地基---》砌砖---》刷墙我们把这三个步骤,按照上述顺序给排一下序,就是所谓的拓扑排序。那么在图中就是没有箭头指向的节点是最先开始的。也就是入度为0的点。当进行完一个入度为0的点时,我们把它的影响消掉,就是它箭头指向的节点的入度要减1。然后再从剩余的节点中寻找是否有入度为0的点,如果有的继续循环。 public static List<Node>原创 2022-03-16 10:54:05 · 804 阅读 · 0 评论 -
图的深度优先遍历
利用栈实现。从源点开始按照深度把节点放入栈中,然后再弹出。每弹出一个节点,把该节点一个没有进入栈中的节点放入栈中,并把该节点也放入栈中,方便回溯。直到栈变空。public static void dfs(Node node) { if(node == null) { return; } Stack<Node> stack = new Stack<Node>(); HashSet<Node> set = new HashSet<原创 2022-03-16 10:24:56 · 1085 阅读 · 0 评论 -
折纸问题的求解(递归满二叉树)
在折叠了n次之后,就是一个二叉树的中序遍历结果。 public static void printAllFolds(int N) { printProcess(1, N, true); } public static void printProcess(int i,int N,boolean down) { if(i>N) { return ; } //上边是凹,中序遍历 printProcess(i+1, N, true); System.out...原创 2022-03-15 15:37:34 · 1164 阅读 · 0 评论 -
二叉树的序列化与反序列化
将内存中一棵树变成字符串的形式,然后再将这一段字符串变成一棵树。可以说就是讲遍历一遍后的结果用一个字符串保存下来。可以使用先序遍历、中序遍历、后序遍历、层序遍历都行。 //序列化一个二叉树 public static String NodeToString(NodeTwo head) { if(head == null) { return "#_"; } String s = head.value+"_"; s += NodeToString(head.left);原创 2022-03-15 15:20:19 · 1162 阅读 · 0 评论 -
二叉树中找到一个节点的后继节点
在二叉树中的中序遍历中,node节点的后一个节点叫做node的后继节点。在常规的二叉树中,直接按照中序遍历走一遍。如果二叉树中每个节点多了一个parent属性,那么时间复杂度便可以下降很多。1.如果这个节点存在右子树,那么这个节点的后继节点就是右子树的最左节点。2.如果这个节点没有右子树,那么这个节点的后继节点就是它的第一个祖先节点的父节点(这个祖先节点要是它父节点的左孩子)也就是说x 的后继节点就是y,对于Y来说x就是它左子树的最后一个节点。遍历完左子树接着就是遍历Y。原创 2022-03-15 10:45:42 · 1596 阅读 · 0 评论 -
二叉树给定两个节点,找出他们最低公共祖先节点
思路:先找出所有元素的父节点,使用HashMap进行存储。找出一个节点的父节点的结合。使用HashSet进行存储。然后另外一个节点就从下到上依次找每个父节点,看另一个父节点集合中是否包含此节点,如果包含就输出。 public static NodeTwo findLowNode(NodeTwo head,NodeTwo node1,NodeTwo node2) { HashMap<NodeTwo,NodeTwo> fatherMap = new HashMap<Nod原创 2022-03-14 21:22:25 · 714 阅读 · 0 评论 -
二叉树递归套路--判断二叉树是否是平衡二叉树、搜索二叉树和满二叉树
什么是平衡二叉树?平衡二叉树就是说,对于二叉树的每一个节点,左右子树的高度差小于等于1。这样的二叉树被称为平衡二叉树。怎么递归?二叉树要满足平衡需要做到三点:1.左子树是平衡二叉树2.右子树是平衡二叉树3.左右子树的高度差小于等于1.那么我们需要子树传递的信息就包含两个方面:是否是平衡二叉树和高度。如果子树可以把这两个信息传递给根节点,我们就可以判断出这个二叉树是否是平衡二叉树。public static boolean isBanlanced(NodeTwo he原创 2022-03-14 15:54:07 · 320 阅读 · 0 评论 -
完全二叉树的判定
如果满足两个条件就可以说这棵树是完全二叉树1.一个节点如果有右孩子就一定有左孩子。2.按照宽度优先遍历,在满足1的条件下如果有一个节点的左右子节点不双全的话,那么后面的节点一定都是叶子节点。。宽度优先遍历可以使用队列进行遍历。先将头结点放入队列中。如果队列不为空,取出一个节点,打印,如果有左节点就将左节点放入队列中,如果有右节点就将右节点放入队列中,直到队列为空。 public static boolean checkCST(NodeTwo head) { boolean fl原创 2022-03-13 17:11:34 · 741 阅读 · 0 评论 -
判断一棵二叉树是否是搜索二叉树
搜索二叉树的判定原创 2022-03-13 16:41:39 · 1245 阅读 · 0 评论 -
判断一个单链表是否是回文。
回文就是从左往右和从又往左都是一样的数。方法1:将每一个节点都入栈,然后再从链表的头部开始和栈中的元素进行比对,弹出一个比对一次,如果每次比对都相等,就说明是回文的。 public static Boolean isReverse(SingleLinkList list) { Node temp = list.head.next; Stack<Node> stack = new Stack(); while(temp!=null) { stack.add(temp原创 2022-03-11 17:06:26 · 969 阅读 · 0 评论 -
双指针的思路
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。思路:给定两个指针,都在0位置上,如果是等于零就快指针加1,如果是不等于零,就交换快慢指针的值,然后快慢都加1.例如:交换快慢指针的值,然后都加1.是零所以快指针移动一下非零,所以交换值,并且都加1.这个题目可以这样理解:慢指针后面的一块地方是已经处理好的,快指针指向的是待处理的数开始的时候是没有数字已经处理好的,然后把1放进了这个处理好的范围里...原创 2022-03-10 15:04:04 · 170 阅读 · 0 评论 -
双指针的使用--有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。我们可以定义两个指针,R指针指向大于等于0的第一个位置,L指针=R-1L指针向左移动,R指针向右开始移动,比较两个指针元素平方的大小,小的加入到新的数组中去,就和归并排序是差不多的。 public static int[] sortedSquares(int[] nums) { int i; for(i=0;i<nums.length;i++.原创 2022-03-09 17:25:55 · 172 阅读 · 0 评论 -
单向链表的反转
例如之前链表中有 1,3,5,7,9反转就变成9,7,5,3,1思路:新定义一个头结点,从原链表中遍历节点,每次遍历一个节点就将这个节点插入到定义的那个新节点之后。就比如原本是这样的遍历到1就将1插入到新头结点后面遍历到3也是插入到头结点后面后面的都是一样的这样就可以完成反转了 public void reverseList() { if(head.next==null || head.next.next==null) { return ; } N原创 2022-03-08 23:57:39 · 213 阅读 · 0 评论 -
快慢指针,求链表中倒数第k个元素
一个指针先走到k位置,然后两个指针同时走,当快的指针走到了最后时,慢的指针也就到了倒数第k个元素位置。 public Node findLastIndexNode(int index) { Node temp = head.next; Node temp2 = head.next; if(index<=0) { System.out.println("输入错误");//只能是倒数第一第二这样 return null; } int i=1; for(;i<原创 2022-03-08 15:46:48 · 142 阅读 · 0 评论 -
哈希表和有序表的简单使用
哈希表有两种形式HashSet和HashMapHashSet只有key,HashMap中有key和value。结构都是一样的,没有区别。public static void main(String[] args) { HashSet<Integer> hashSet1 = new HashSet<>(); hashSet1.add(2); System.out.println(hashSet1.contains(2)); hashS原创 2022-03-07 22:06:01 · 162 阅读 · 0 评论 -
工程中对排序的改进
例如在快排中,选择在数据量小的时候是使用插入排序,数据量大的时候使用快排。就相当于两个算法拼在一块来用。插入排序的常数量小,数据量小的时候,排序算法其实是更快的。归并算法也是一样的。充分利用不同算法之前的优势。排序时,使用系统给的排序算法是怎样工作的?1.如果数组是基本数据类型,那么系统就会使用快排,因为基本数据类型是不需要考虑稳定性的。2.如果是自定义类,那么系统就会使用归并排序,因为归并排序是稳定的,自定义类的稳定性排序是有意义的。...原创 2022-03-07 21:17:35 · 78 阅读 · 0 评论 -
排序的稳定性
首先说一下排序的稳定性有什么作用。在基础类型中,比如int,char,double类型中稳定性是没有什么用的,两个数交换次序不会有什么影响。但是在一些自定义类的对象进行排序的时候,稳定性的作用就显现出来了。例如学生有年龄和班级两个属性,首先对一批学生的年龄进行排序,从低到高。然后再对学生的班级进行排序,班级小的排在前面。排序算法如果是不稳定的,那一个班级内的学生的年龄不一定是有序的,而排序算法是稳定的话,整体做到班级按从小到大排序,而班级内部也是从小到大排序的。生活中用到稳定的排原创 2022-03-07 21:02:40 · 156 阅读 · 0 评论 -
排序算法--基数排序
基数排序的思想就是先对个位进行排序,然后再十位进行排序,再百位进行排序,排到最大的一个数的最高位停止,那么这个数组也就排好了。例如我们对上面这个数组进行排序,先个位排序,个位数是几就放到相对于的桶里面先进先出原则,然后再依次放回到数组中然后再进行十位的排序然后再放回到原数组中数组中最大数最高位是百位,所以需要排到百位。没有百位就补零最后一次按照先进先出将数组中的元素拿出来。这样整个数组就排好序了。先进先出是为了保证排序的稳定性。下面放代码..原创 2022-03-07 18:26:01 · 312 阅读 · 0 评论 -
排序算法--计数排序
计数排序就是新开一个数组,统计需要排序数组中每个元素出现的次数。然后再根据新开数组中统计好的次数,将排序数组排好。但是这个非比较排序算法是有一定限制的。首先得知道排序数组的范围,才好重新开一个数组,新开数组的大小是原数组加1 。例如一个数组中的数据代表意义是年龄,年龄是存在范围的(0~150),那么就可以对这个数组进行计数排序。package com.yf;public class Day08 { public static void countSort(int[] arr,int la原创 2022-03-06 22:30:48 · 156 阅读 · 0 评论 -
java比较器
系统中自带的Arrays.sort()默认比较器是从小到大排序的,但是我们有时候需要从大到小排序,又或者我们需要比较对象,这时候就需要用到比较器了。代码中比较器就是一个类重载了Comparator中的compare方法 public static class Acomp implements Comparator<Integer>{ // @Override public int compare(Integer arg0, Integer arg1) {原创 2022-03-06 01:03:27 · 220 阅读 · 0 评论 -
堆排序--优先级队列例题
已知一个几乎有序的数组,几乎有序是指,如果把数组排好序的话,每个元素移动的距离不超过K,并且k相对于数组来说比较小。请选择一个合适排序算法针对这个数据进行排序。原创 2022-03-05 21:43:30 · 379 阅读 · 0 评论 -
排序算法-堆排序
在学堆排序之前得先明白什么是完全二叉树。完全二叉树就是从上到下,从左到右中间没有间隔的二叉树。例如这样可以称为完全二叉树,这样就不行了,那么在了解了什么是完全二叉树之后,我们就可以把数组展开成一个完全二叉树。就像这样。我们再来了解一个概念【大根堆】大根堆就是父节点是要大于子节点的,左右兄弟节点没有要求。那么一个数组如何转化为大根堆呢?例如数组{1,4,3,2,7,9},我们假设从数组中一个一个数拿出来进行大根堆。先拿出来1,就一个数肯定满足大根堆原创 2022-03-05 20:58:29 · 142 阅读 · 0 评论