1.两数之和
1.1 暴力循环
万物皆可使用循环破解。
思路:两层循环,第一层找第一个变量,第二层找第二个变量。再判断两个变量之和是否与target相等,相等则返回下标。不等返回空数组。
public int[] twoSum(int[] nums, int target){
for(int i = 0, i < nums.length; i++){
for(int j = i + 1; j < nums.length; j++){
if(nums[i] + nums[j] == target){
return new int[]{i, j};
}
}
}
return new int[0];
}
暴力双层循环
1.2 哈希表
思路:哈希表获取、插入、删除数据的复杂度为O(1)。当数组中的数据存在于哈希表中时,我们可以将数组值作为key,数组索引作为value来存储。后面判断数组值和是否为target时,取出数组值对应索引即可。
public int[] twoSum(intp[] nums, int target){
Map<Integer, Integer>map = new Map<>();
for(int i = 0; i < nums.length; i++){
int sub = target - nums[i];//差值
if(map/containsKey(sub)){
return new int[]{i, map.get(sub)};
}
map.put(nums[i], i);
}
return new int[0];//返回空数组
}
哈希表
3.最长的不重复子串
思路:Map中的key是不重复的,无序的。因此可以用来判断字符串中的元素是否重复。如果map中已存在以字符为key,字符对应的索引为value的元素,那么就更新重复字符计数的变量值。如果不存在,则加入map,并更新不重复计数的最大长度。
Public int findMaxLen(String s){
int left = 0, maxLen = 0;
Map<Character, Integer>map = new HashMap<>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(map.containsKey(c)){
left = Math.max(left, map.get(c)+1);
}
map.put(c, i);
maxLen = Math.max(maxLen, i-left+1);
}
return maxLen;
}
最长的不重复子串
.二分查找有序数组的目标值,并返回索引
使用一层while循环,将更新的中间索引对应的数组值与目标值比较,如果相等直接返回中间索引值;如果小于则更新右边位置为原中间索引前一个位置;如果大于则更新左边位置为原中间索引的后一个位置。
public int search(int[] nums, int target) {
int left = 0, right = nums.length-1;
while(left <= right){
int mid = (right - left)/2 + left;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < target){
left = mid + 1;
}else{
right = mid -1;
}
}
return -1;
}
二分查找指定值
20.有效括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
思路:当字符串中字符为左括号时,进栈对应的右括号。当字符串为右括号是,如果栈为空或字符不与出栈字符相等则返回false。循环结束后,若栈为空,返回true,不然返回false。
public boolean isValie(Sting s){
Deque<Character>stack = new LinkedList<>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c == '('){
stack.push(')');
}
else if(c == '['){
stack.push(']');
}
else if(c == '{'){
stack.push('}');
}
else if(stack.isEmpty() || c != stack.pop()){
return false;
}
}
if(stack.isEmpty()){
return true;
}
return false;
}
有效括号
21.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
思路:创建一个新的链表,当两个链表都不为空时,选择小的值加在新链表的末尾。更新新链表。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode cur = pre;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
//遍历完后,谁还有值直接放在新链表结尾
cur.next = l1 == null ? l2 : l1;
return pre.next;
}
合并两个有序链表
70.爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
思路:当楼梯层数为1或2时,直接返回其爬法。当层数从3开始时,当前层数的爬法由前两层楼梯爬法的和。这种思想其实是跟动态规划的思想类似。即问题的节由子问题的解组成。利用已知问题的解求出所需问题的解。
public int climbStairs(int n){
if(n == 1 || n ==2){
return n;
}
int first = 1, second = 2, sum = 0;
for(int i=3; i<=n; i++){
sum = first + second;
first = second;
second = sum;
}
return sum;
}
爬楼梯
94 中序遍历(递归)
思路:通过List存储中序遍历结果,使用递归思想,遍历树中每个值首先判断根节点是否为空,不空则从左子树开始,然后列表添加根节点值,最后到右子树
public List<Integer>inorderTraversal(TreeNode root){
List<Integer>res = new ArrayList<>();
return inorder(root, res);
}
public List<Integer>inorder(TreeNode root, List<Integer>res){
if(root == null){
return res;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
return res;
}
中序遍历
101 对称二叉树
思路:都从根节点出发,分为左边部分和右边部分,若根节点都为空则返回true,若一个为空则返回false。最后比较两个根节点的值,一个根节点的左边值与另一个节点的右边值以及一个节点的右边值与另一个节点的左边值。
public boolean isSymmetric(TreeNode root){
return check(root, root);
}
public boolean check(TreeNode p, TreeNode q){
if(p == null && q == null){
return true;
}
if(p == null || q == null){
return false;
}
return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);
}
对称二叉树
102 树的层次遍历
思路:通过队列来存储每一层的节点,当节点存在左子结点和右子节点就进队。每次层次遍历都有一个列表,所以定义一个level列表记录每一层的遍历结果。并将其长度作为遍历次数来更新新的节点进队
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>>res = new ArrayList<>();
if(root == null){
return res;
}
Queue<TreeNode>queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer>level = new ArrayList<>();
int curSize = queue.size();
for(int i = 0; i < curSize; i++){
TreeNode cur = queue.poll();
level.add(cur.val);
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right!= null){
queue.offer(cur.right);
}
}
res.add(level);
}
return res;
}
层序遍历
104.二叉树最大深度
给定一个二叉树 root
,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
思路:利用递归思想,让左右节点比较大小,最后加上1表示根节点到最长节点的距离。
public int maxDepth(TreeNode root){
if(root == null){
return 0;
}
return Math.max(maxDepth(root.left), maxDepth(root.right))+1;
}
二叉树最大深度
小结
选择适合的数据结构结合循环语句可以很好地解决算法问题,我们可根据算法题目从中找出与存储结构相吻合的特点,然后通过该数据结构结合语法来实现算法