赞
踩
虽然我们平时用Java 用的比较多,做技术做的也比较不错,但完全要你用语言来写一些东西,你还是会懵逼,因为我们平时都是用IDEA智能提示,可能一个提示,代码就自动写完了,所以,当你用Java 来写一些算法题的实现,有可能你记不起一些语法来。本文来帮你加强编写代码的能力。
数组的特性就是直接根据下标来获取数据,在算法题中很多输入都是数组,那么:
int h = height.length
不带括号哦,记不住就很晕
int[] tmp = new int[arr.length]; //新建一个临时数组存放
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNode first = head; ListNode last = head; for (int i=0;i<n;i++) { last = last.next; } while(last!=null && last.next!= null){ last = last.next; first = first.next; } if (first == head && last == null) { return head.next; } ListNode thr = first.next; first.next = thr.next; thr.next = null; return head; } }
此代码里用到里内部类,就不需要写get、set方法,直接访问内部变量。
(1)字符串长度
s.length()
(2)根据坐标 获得字符
s.charAt(i) , i 是坐标
(3)根据坐标截断 字符
s.substring(sta,end) :不包括end 坐标字符
(4)char 转数字
int num = (int)ch - (int)(‘0’);
int num = cha - ‘0’
直接与字符“0”相减也可以得到。
我们经常会碰到输入一个数字,然后输出一个数组,代码如下:
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
Math.min(a,b)
Math.max(a,b)
虽然三目运算符可以替代这个效果,但是代码可读性不好。
ListNode dummy = new ListNode(0); // 生成一个新节点
dummy.next = head; // 头节点指向下一个节点
这样放方便你避免一些异常情况。
private void swap(int[] arr, int i, int j) {
if (i==j) {return;}
arr[i] =arr[i] ^arr[j];
arr[j] = arr[i]^arr[j];
arr[i] = arr[i]^arr[j];
如果是两个变量,上面交换逻辑没问题,如果是通过坐标来访问指针,必须判断两者是否相等,否则出错。
Arrays.sort(nums);
这两种遍历可以证连贯性,虽然是树的遍历方式,但是在数组中,可以利用这两种遍历方式来回达到连贯性的遍历手段,很不错的方式,但要借助一个二维数组来控制访问的位置,标明那些位置已经被访问了。
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 “回溯” 返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 “回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。所以这里主要包括两个步骤,一个是进、一个是退,额外需要一个数组来存储这个访问的路径。
访问的路径
用一个容器来存储访问的路径上的节点。List 和 Queue
进
访问这个节点,再选择其他节点来继续访问,了解能不能满足条件。
退
去除这个访问的节点,选择其他节点访问。
class Solution { public List<List<Integer>> combinationSum(int[] candidates, int target) { Arrays.sort(candidates); List<List<Integer>> resList = new ArrayList<>(); Deque<Integer> numS = new ArrayDeque<>(); dfs(candidates,target,candidates[candidates.length-1],numS,resList); return resList; } public void dfs(int[] candidates, int target,int max, Deque<Integer> numS,List<List<Integer>> resList){ if(target==0){ resList.add(new ArrayList<>(numS)); return; } for(int i=candidates.length-1;i>=0;i--){ if(candidates[i]>target || candidates[i] > max){ continue; } if(candidates[i]<=target){ numS.add(candidates[i]); dfs(candidates,target-candidates[i],candidates[i],numS,resList); numS.removeLast(); } } } }
回溯的理解,我们可以通过递归来轻松达到for循环的目的,比如我们有内容为1-10的数组,通过递归来深度访问10这个数字,然后从10-1的顺序来反过来循环数组,这也是特别有意思的内容,并且通过此思想来达到回溯方法。
比如:
public void dfs(int pos, int rest) { if (rest == 0) { ans.add(new ArrayList<Integer>(sequence)); return; } if (pos == freq.size() || rest < freq.get(pos)[0]) { return; } // 这一步递归 就是访问数组的最后一个位置 dfs(pos + 1, rest); // 然后从当前pos,一直到最后一个pos位置 int most = Math.min(rest / freq.get(pos)[0], freq.get(pos)[1]); for (int i = 1; i <= most; ++i) { sequence.add(freq.get(pos)[0]); dfs(pos + 1, rest - i * freq.get(pos)[0]); } // 回溯 for (int i = 1; i <= most; ++i) { sequence.remove(sequence.size() - 1); } }
比如我们一个数组有10个元素,就是1-10,上述递归的过程就类似:
当然上述是两个过程,不过很巧的是这两个过程结合起来效果很好。
如果有人要看具体的内容,可以参考:https://leetcode-cn.com/problems/combination-sum-ii/solution/zu-he-zong-he-ii-by-leetcode-solution/ 这个链接看看完整的问题和代码,从而更深的体会。
经典的带有重复数字的数组排列组合问题,数组中有重复的数字,要求数组中的数字排列组合,这些组合不能有重复的组合,这是排列的进阶版,解决方法就是对数组里的数字排序,这样让排序后的数字,同一个位置不能重复出现,排列组合其实就是数字的位置问题,比如:[1,2,2,3,3],最容易咋成的重复组合则是:[2,1,2,3,3],两个2可以互换位置,所以当我们回溯2这个所在的位置,下一个重复的2就不能在选了。
class Solution { public List<List<Integer>> permuteUnique(int[] nums) { Arrays.sort(nums); int n = nums.length; int pre = Integer.MIN_VALUE; List<List<Integer>> res = new ArrayList<>(); for(int i=0;i<n;i++){ if(nums[i]!=pre){ int[] flags = new int[n]; List<Integer> one = new ArrayList<>(); flags[i]=1; one.add(nums[i]); get(nums,res,one,flags); pre = nums[i]; } } return res; } public void get(int[] nums,List<List<Integer>> res,List<Integer> re,int[] flags){ if (re.size() == nums.length){ res.add(re); return; } int n = nums.length; int pre = Integer.MIN_VALUE; for(int i=0;i<n;i++){ // pre 就是上一个数 if(nums[i]!=pre && flags[i]==0){ List<Integer> one = new ArrayList<>(re); one.add(nums[i]); flags[i]=1; get(nums,res,one,flags); pre = nums[i]; flags[i]=0; } } } }
Stack<Integer> st = new Stack<Integer>();
经常用的方法有:empty(),push(),pop()
Queue<String> queue = new LinkedList<String>();
这个也可以实现栈,因为继承了抽象类。
LinkedList既是普通容器,也实现了队列。
Queue<String> queue = new LinkedList<String>();
常用的方法有:
博弈论问题的一个经典就是捡石头,给你一个数组,数组里每一个元素的数值表示这一堆石头数量,你和一个朋友一起捡石头,可以从左边或者右边依次交替捡石头,问你先手捡石头和后手捡石头,石头的最大值是多少,这里说明一下你和朋友都绝顶聪明,都不会乱选石头,这个问题的本质其实就是数组选数值,数组的子集的解也是这个大数组的解,也是一个可以动态规划问题,只不过问题在于交替选,那么如何建模?
所以,先后手用一个class对象来存储,所以一个数组的子集表示为: d p [ i ] [ j ] = s t o n e dp[i][j] = stone dp[i][j]=stone,stone有两个字段,一个是fir:表示先手,一个sec :表示后手。
子集的逻辑:
可以采取递归或者数组倾斜遍历来实现。
位运算主要有几个技巧点:
相同的数字异或运算等于0:
10&10=0
a ^=b;
b=a^b;
a=b^a;
字母编码从 65开始:A 表示 65,26个大写英文字母,到90,小写a从 97开始,这里注意空格是32,"_"是95,
用 -10 来填充数组
int[] a = new int[4];
Arrays.fill(a,-10);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。