![](https://img-blog.csdnimg.cn/20201014180756922.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
算法题笔记
算法笔记
魔幻音
如果世界上真的有奇迹,那只是努力的另一个代名词。
展开
-
基础算法:发牌算法、快慢指针判断循环
发牌算法当情况需要等概率的随机分配数据,可以尝试发牌算法(时间复杂度O(n)) for (int i = 0; i < res.length; i++) { swap(res, i, i + random.nextInt(n - i)); }思想很简答,就是从头到尾进行遍历,将当前位置与其后随机位置值进行替换,便利完成后就得到相对公平的数据随机分配。判断循环循环可以用Set集合的不可重复或递归推值进行判断,但可能由于数据过大导致递归栈溢出或集合过大原创 2022-05-26 15:23:04 · 188 阅读 · 0 评论 -
基础算法:位运算相关题目
数字范围按位与给定初始数字到结尾数字,求其所有数字按位与的结果。public int rangeBitwiseAnd(int left, int right) { int head = 1 << 30; int res = 0; while (head > 0 && ((left&head) == (right&head))) { res |= left&head;原创 2022-05-18 15:58:36 · 84 阅读 · 0 评论 -
基础算法:动态规划相关题目
动态规划实际相当于每次遇到的情况处理都相同,那么处理n次和处理1次过程都是一样的,重心就是找到每个题目下的状态转移方程,依据方程进行逻辑编写即可。打家劫舍 II相邻房子不能都打劫,且所有房子环形建设。public int rob(int[] nums) { if (nums.length == 1) { return nums[0]; } return Math.max(myRob(Arrays.copyOfRange(nums原创 2022-05-12 17:44:24 · 200 阅读 · 0 评论 -
基础算法:递归/回溯相关题目
子集在给定数组中做出所有子集。public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> res = new LinkedList<>(); List<Integer> temp = new LinkedList<>(); backTrack(0, nums, res, temp);原创 2022-05-10 00:33:52 · 213 阅读 · 0 评论 -
基础算法:深/广度优先遍历及相关题目
深度优先遍历通常采用方法递归实现,主要思路是找到一条路后一头钻到底(符合递归方法中只有触底return后,才会执行后续语句)(堆栈也可实现)广度优先遍历通常采用队列辅助实现,主要思路是遇到一个节点后,将其周遭所有符合条件的节点都记录起来,逐个处理。岛屿数量岛屿定义:数组由1 0组成,一整片1即为一个岛屿。①深度优先遍历:public int numIslands(char[][] grid) { int res = 0; for (int i = 0; i &原创 2022-05-06 00:17:44 · 1947 阅读 · 0 评论 -
基础算法:滑动窗口相关题目
找到字符串中所有字母异位词题目给出两个字符串words和compare,在words中查找包含字符数量和compare相同的字字符串起始下标。public List<Integer> findAnagrams(String s, String p) { List<Integer> res = new ArrayList<>(); int[] words = new int[26]; int[] compare = new原创 2022-05-05 00:28:54 · 155 阅读 · 0 评论 -
算法基础:双指针相关题目
删除链表中相同元素public ListNode deleteDuplicates(ListNode head) { ListNode res = new ListNode(0, head); ListNode cur = res; while (cur != null && cur.next.next != null) { if (cur.next.val == cur.next.next.val) {原创 2022-05-03 09:29:57 · 1058 阅读 · 0 评论 -
算法基础:二分查找及相关题目
二分查找二分查找解析注:①二分查找个人感觉分为查找边界和具体值两种情况,相比来说具体值就是将nums[mid] == target作为截止条件跳出,查找边界就按头尾指针相遇为截止。②普通二分使用mid = (head + tail) / 2;有可能产生溢出,可以用mid = head + ((tail - head) / 2);防止溢出情况(除二操作还可用>>1右移位运算优化)在排序数组中查找元素的第一个和最后一个位置题目实际就是在有序数组中,查找目标元素起始位置和终点位置。查看目标原创 2022-04-30 23:59:47 · 330 阅读 · 0 评论 -
最长回文子串判断
方法一:暴力枚举所有子串,进行回文串判断并获取最长。public boolean isPalindromic(String s) { int len = s.length(); for (int i = 0; i < len / 2; i++) { if (s.charAt(i) != s.charAt(len - i - 1)) { return false; } }原创 2022-04-10 21:03:49 · 266 阅读 · 0 评论 -
常见查找算法(顺序查找、二分查找)
①顺序查找:顺序查找本身就是暴力遍历进行判断。public static int sequentialSearch(int[] a, int key) { for (int i = 0; i < a.length; i++) { if (a[i] == key){ return i; } } return -1; }在此之上因为for循环每次还要去比较是否越界原创 2022-03-23 15:00:29 · 585 阅读 · 0 评论 -
常见排序算法Java实现
①冒泡排序:public int[] bubble(int[] nums) { if (nums.length == 0) { return nums; } for (int i = 0; i < nums.length; i++) { for (int j = 0; j < nums.length - i - 1; j++) { if (nums[j] > n原创 2022-03-23 14:26:03 · 853 阅读 · 0 评论 -
LRU实现(手动双向链表+HashMap;LinkedHashMap)
方法一:手动整体需要两个模块,HashMap用于存储key及其对应值,链表用于存储各个节点使用时间排序(因为要频繁删除、添加,用双向链表方便操作,时间复杂度降到O(1))首先构造双向链表节点类:class Node { int val; int key; Node pre; Node next; public Node(int key, int val) { this.key = key;原创 2022-03-23 00:11:24 · 409 阅读 · 0 评论 -
寻找两个正序数组的中位数(暴力解法,双指针同时遍历,寻找第K小的数)
暴力解法:将两个有序数组合并起来,然后取中位数。public double findMedianSortedArrays(int[] nums1, int[] nums2) { int l1 = nums1.length; int l2 = nums2.length; if (l1 == 0) { return ((l2 % 2) == 0 ? ((nums2[l2 / 2] + nums2[l2 / 2 - 1]) / 2.0)原创 2022-03-22 16:01:07 · 229 阅读 · 0 评论 -
无重复字符的最长子串(滑动窗口)
题目要求检测出给定字符串的一个子串(该子串不能含有重复出现的字符)想法:用HashMap记录出现的字符以及其最后一次出现的下标,用containsKey检测重复性,若重复,则将滑动窗口的左边界移动到最后一次出现该字母的位置;遍历字符无论是否遇见过,都应该更新map中该字母的下标及最长子串的长度。代码:public int lengthOfLongestSubstring(String s) { Map<Character, Integer> used = new Hash原创 2022-03-22 14:31:52 · 112 阅读 · 0 评论 -
两数相加(两链表按序相加)
解:同时遍历两个链表,直至两个都遍历完结位置(当前节点为空时,值按0处理),两节点值及进位值相加并得出进位、和,赋值给当前新节点,以生成结果链表。public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode cur = new ListNode(); ListNode pre = cur; int carry = 0; while (l1原创 2022-03-14 15:54:07 · 644 阅读 · 0 评论 -
复杂链表的复制(额外带一个random)
方法一:利用图存储每一个节点的属性,键为原链表,值为新链表,先遍历一边链表,依据原链表生成新节点,再遍历一边链表,在图中用原节点取出新节点并且依据原节点关系给新节点赋予next和random关系。public Node copyRandomList(Node head) { if (head == null) { return null; } Map<Node, Node> vals = new HashMap<&原创 2021-08-30 23:09:33 · 190 阅读 · 0 评论 -
斐波那契数列、小青蛙跳台阶
方法一:用列表存储已经计算好的数字,省去每次递归求所需数据过程:public int fib1(int n) { List<Integer> nums = new ArrayList<>(); nums.add(0); nums.add(1); for (int i = 2; i <= n; i++) { nums.add((nums.get(i - 1) + nums.get(i -原创 2021-07-26 11:16:35 · 154 阅读 · 0 评论 -
特殊二维数组找数(set单值,暴力遍历查找,转换对二维矩阵的看法近似二叉树,将二维数组转为坐标系)
该二维数组特殊处是行、列都为增序排列。方法一:暴力解法,遍历数组查找该值:public boolean findNumberIn2DArray(int[][] matrix, int target) { if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { return false; } int rows = matrix.length, colu原创 2021-07-22 19:04:11 · 191 阅读 · 0 评论 -
数组中出现次数超过一半的数字(摩尔投票)
方法一:用map记录数字和出现次数并判断:public int majorityElement1(int[] nums) { Map<Integer, Integer> map = new HashMap<>(); for (int num : nums) { if (map.containsKey(num)) { int time = map.get(num);原创 2021-07-30 17:07:07 · 131 阅读 · 0 评论 -
数字重复处理(数组下标赋值,set集合单次,原地交换)
题目:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。我的解法:第一版:将数组中的元素看做新数组下标,每遇一个值,就对新数组的下标进行增值,最后再将新数组遍历一遍,查找出值>1的数,下标即为出现多次的数。class Solution { public int findRepeatNumber(int[] nums) { int[] t原创 2021-07-22 18:38:44 · 158 阅读 · 0 评论 -
在排序数组中查找数字个数、0~n-1缺少的数字(二分法)
遇到有序数组,第一反应就是二分法。二分法主要思路:分别在数字头和尾建两个指针,重点比较两个指针中位值与目标值的大小,当中央值比目标值小,说明目标值在中值右边,则可以直接不考虑中值左侧的那些值。在排序数组中查找数字个数:方法一:普通二分法:public int search(int[] nums, int target) { int i = 0; int j = nums.length - 1; while (i <= j) {原创 2021-08-05 16:01:13 · 585 阅读 · 0 评论 -
翻转单词顺序(双指针)
方法一:因为单词需要按空格划分,利用字符串的库函数进行划分,再将获取到的字符串数组倒序遍历组成新的字符串即可。public String reverseWords(String s) { String[] words = s.trim().split(" "); StringBuilder stringBuilder = new StringBuilder(); for (int i = words.length - 1; i >= 0; i--)原创 2021-08-08 15:42:22 · 456 阅读 · 0 评论 -
剑指offer:空格替换
方法一:用replace方法public String replaceSpace(String s) { return s.replace(" ", "%20"); }方法二:将字符串转为字符数组,遍历查找替换。public String replaceSpace1(String s) { StringBuffer stringBuffer = new StringBuffer(); for (char a : s.toCharArray(原创 2021-07-22 19:33:25 · 38 阅读 · 0 评论 -
用两个堆栈实现队列
方法一:既然堆栈是先入后出,那么两个堆栈第一个用于加入尾数据时存储,第二个用于删除头数据时将第一个堆栈中的元素按先入后出再加入第二个堆栈,这样第二个堆栈顶部就是当前队列的头元素,此时就可以删除头元素。删除后再将第二个堆栈元素放回第一个堆栈继续添加数据的操作。LinkedList<Integer> stack1; LinkedList<Integer> stack2; public CQueue() { this.stack1 = new Link原创 2021-07-23 18:42:39 · 312 阅读 · 0 评论 -
调整数组顺序使奇数位于偶数前面(左右指针,快慢指针)
方法一:左右指针,目的把奇数放在数组前面,所以定义两个指针,分别从左右开始遍历数组。左指针:遍历直到遇到偶数右指针:遍历直到遇到奇数当左为偶数,右为奇数,进行调换即可。public int[] exchange2(int[] nums) { int left = 0; int right = nums.length - 1; int tmp; while (left < right) { if ((num原创 2021-07-28 16:19:05 · 199 阅读 · 0 评论 -
机器人的运动范围(数位和、可达解、深度优先遍历、广度优先遍历)
题目要求从0,0开始移动,每次移动的格位要求数位和相加小于阈值。首先数位和的求法:private int bitSum(int n) { int sum = 0; while(n > 0) { sum += n % 10; n /= 10; } return sum; }利用取余和除法达到目的。另一种:因为该题是从0,0开始递增的,并不需要突兀的求某个大值的数位和,那么就原创 2021-07-27 15:13:26 · 231 阅读 · 0 评论 -
和为s的连续正数序列(滑动窗口)
题目要求得出和为s的所有连续整数序列。想法:双指针形成滑动窗口,时刻记录双指针间的和。当和与目标值相同,则将双指针间的数据记录下来,并且将左指针右移。当和大于目标值,则将左指针右移(将和减小),且和需要减小原左指针值。当和小于目标值,则将右指针右移(将和增大),且和需要减小原右指针值。public int[][] findContinuousSequence(int target) { int i = 1; int j = 2; int sum =原创 2021-08-08 15:18:19 · 80 阅读 · 0 评论 -
反转链表(修改指向)
方法一:public ListNode reverseList(ListNode head) { ListNode cur = head; ListNode next = null; while (cur != null) { ListNode pre = cur.next; cur.next = next; next = cur; cur = pre;原创 2021-07-29 15:28:09 · 150 阅读 · 0 评论 -
矩阵中找路径(DFS深度优先搜索+剪枝)
在矩阵中查找对应单词(注意不能走回头路)。深度优先搜索:按着一个方向走到底,直到到头都没有结果时,回溯到先前节点继续换个方向深入。剪枝:就是在深度优先搜索前,将一些必定不满足条件的路径删掉,防止白白遍历。(如在本题中,将当前字母不同于单词的路径直接不用遍历)在用深度优先搜索之前,我的想法是直接从第一个字符开始遍历二维数组进行字符判断,如果字符相同,再在该字符的上下左右四个位置进行判断。这种简单思路有两个问题:①如果不将遍历过的字符进行标记,会造成走回头路的情况,是题目不允许的。②当二维矩阵出现部原创 2021-07-27 11:30:52 · 597 阅读 · 0 评论 -
打印从 1 到最大的 n 位数
方法1:先计算n位数最大数字,创建数组,用循环加入数值。public static int[] printNumbers(int n) { int max = (int ) Math.pow(10, n) - 1; int[] nums = new int[max]; for (int i = 1; i < max; i++) { nums[i - 1] = i; } return nums;原创 2021-07-28 15:14:00 · 189 阅读 · 0 评论 -
旋转数组的最小数字
方法一:暴力遍历查找: public int minArray(int[] numbers) { int min = numbers[0]; for (int i : numbers) { if (i < min) { return i; } } return min; }方法二:因为旋转数组的本身是一个有序数组,可用二分查找:public原创 2021-07-26 11:48:42 · 66 阅读 · 0 评论 -
约瑟夫环(三种实现,标记、假循环链表、动态规划)
方法一:用数组下标表示每个人的位置,数组元素0代表环内、1代表出环。public int lastRemaining(int n, int m) { int[] arr = new int[n]; int num = 0; int index = 0; int count = n; int res = 0; while (count > 0) { num += (arr[inde原创 2021-08-09 12:18:09 · 408 阅读 · 0 评论 -
倒序输出链表(递归思想)
给出一个自定义的链表:public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } }要求将给的链表翻转放入int数组。方法一:遍历取出数放入一个list,再依据list大小生成数组,再翻转置进去数值。public int[] reversePrint1(ListNode head) { List<Integer> num原创 2021-07-23 16:05:54 · 1345 阅读 · 0 评论 -
两个链表第一个公共节点
方法一:遍历链表,把每个节点加入Set,利用单值可实现查找公共节点。方法二:将公共部分看作C,第一个链单独节点为A,第二个链单独节点为B当两个遍历头都走过A+B+C节点,则可相遇到第一个公共节点。即遍历头A遍历A链,遍历完后开始遍历B链,当两个遍历头都遍历完自身长度,再遍历一次对方独立节点长度就可以相遇。public ListNode getIntersectionNode1(ListNode headA, ListNode headB) { if (headA == null原创 2021-08-05 15:44:10 · 65 阅读 · 0 评论 -
二进制中1的个数(位运算)
给定一个二进制串表达一个数字,求其中有多少个1。方法一:利用位运算遍历。挪遍历位:public int hammingWeight1(int n) { int res = 0; for (int i = 0; i < 32; i++) { if ((n & (1<<i)) != 0){ res++; } } return res;原创 2021-07-28 11:37:27 · 311 阅读 · 0 评论 -
重建二叉树(无重复值)
给出需要重建的二叉树的前序遍历和中序遍历,需要还原该二叉树。思路:前序遍历:【根】【左子树】【右子树】中序遍历:【左子树】【根】【右子树】故前序遍历的第一个元素就是根的数值,获取根植后在中序遍历中就可以找到左、右子树个数等信息了。对于二叉树生成需要如下信息(二叉树根的下标是基于前序遍历生成的,中序遍历用于已知根后,判断该二叉树所包含的左右子树所用范围):函数TreeNode build(int root, int left, int right)参数意义:root:根节点在前序遍历中的下标l原创 2021-07-23 18:06:31 · 98 阅读 · 0 评论 -
扑克牌中的顺子(固定个数判断连续通用)
题目:给定5个数,判断是否是连续数字,其中0可以替代任意数。方法一:暴力方法就是对数组进行排序后遍历,记录0的个数判断连续。方法二:因为数组个数是固定的,那么若数组连续,则数组中最大数-最小数应等于4,但此处额外有0代替任意数的存在,那么最大数-最小数还可以小于4。例如1 2 3 0 0,最大数-最小数只要小于4,剩余部分可以由0补充。public boolean isStraight1(int[] nums) { Set<Integer> num = new Ha原创 2021-08-08 16:16:42 · 324 阅读 · 0 评论 -
和为s的两个数字(二分法)
思路和上篇二分法文章相同。因为数组是排序好的,将头尾值相加,若结果大于目标值,将尾节点前置一位(和数减小),若小于目标值,将前节点后置一位(和数增大)public int[] twoSum(int[] nums, int target) { int i = 0; int j = nums.length - 1; while (i <= j) { int sum = nums[i] + nums[j];原创 2021-08-05 16:04:57 · 82 阅读 · 0 评论 -
两数之和(遍历及存储)
方法一:暴力遍历,用二重循环判断当前值的和,直到找到目标。public int[] twoSum(int[] nums, int target) { int n = nums.length; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (nums[i] + nums[j] == target) {原创 2021-08-09 17:02:58 · 135 阅读 · 0 评论 -
所有奇数长度子数组的和(暴力三重循环,前缀和求值,单独计算贡献)
方法一:从数列头开始,将以该元素为开头的奇数长度子数组遍历求和,所以需要遍历该数列、计算以该元素为开头的数列长、遍历该奇数长数组求和。public int sumOddLengthSubarrays(int[] arr) { int sum = 0; //遍历该数列 for (int i = 0; i < arr.length; i++) { //计算以该元素为开头的数列长 for (int j = 1原创 2021-08-29 22:16:30 · 301 阅读 · 1 评论