算法两周总结
两周前才开始刷算法,其实已经有点晚了,但种一棵树最好的时候是十年前其次就是现在;以及才刷了两周就能感觉到自己的进步了,虽然很菜但起码不是当初那种一看到题目就懵逼的状态了。好了话不多说开始总结
类型
两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
这是我做的第一个算法题,其实比较简单,用人脑可以理解的话来说,遍历整个数组(i),然后在其中寻找两数之和为target的(j)。
eg:首先i=1;然后再在数组大于i的位数寻找j,使i+j = target;
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result = new int[]{0, 1};
if (nums.length == 2) {
return result;
}
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
result[0] = i;
result[1] = j;
return result;
}
}
}
return result;
}
}
总结:这道题虽然一开始花了很多时间,但回过头来想真的很简单。其实我一直觉得java基础好不好很影响算法解题速度的,因为调试肯定是用ide,这道题都是在学怎么写这个main方法,难度反而不是算法本身。。。
说来尴尬,new一个int也是我这个算法里面的学习内容;
手撕算法我就真的很慌
有效的括号
*给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。*
这道题我一开始就想错了,我居然会觉得这个东西时回文字符串。。。程序员果然是要有火眼金睛才能干的下去啊。然后我发现自己想错之后我还是没有思路,因为我不知道栈是什么,所以这道题最后是看了答案才写出来的。
class Solution {
public boolean isValid(String s) {
// 首先一定要是偶数,不然不可能的
// 布尔值表达为true,执行if语句中的代码块,否则执行if后面的代码块
if ((s.length()) % 2 != 0) {
return false;
}
Stack<Character> stack1 = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(') {
stack1.push(')');
}
else if (c == '{') {
stack1.push('}');
}
else if (c == '[') {
stack1.push(']');
}
// 如果stack在这里是空的,那说明没有上述符合要求的,如果stack.pop不符合要求,说明也不一样
// 我大概懂了,就是应该全部push完毕然后再检查这个,不然push一个pop一个那永远都是false啊
else if (stack1.empty() || stack1.pop() != c) {
return false;
}
//最终走到这里stack里面的东西都要pop出来,所以stack必定为空,如果没有pop出来就说明存在没有配对的情况,直接false
}
return stack1.empty();
}
}
对了,我甚至不知道布尔值表达为true,执行if语句中的代码块,否则执行if后面的代码块。。。
还有就是没搞懂while和if的区别
还有就是要注意前置条件,有的时候我压根没写,看答案才发现的
合并有序列表
*将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 *

class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}else if(l2 == null){
return l1;
} else if(l1.val<l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
第一次做链表也是第一次接触指针,其实在我学习java的这么长的时间里面都没有正儿八经的接触过指针;然后也是趁着这个时候去了解了一下java为什么没有和c++一样的指针:首先是为了安全性,其次ajava有类似指针,在java中称为引用。所谓的引用就是内存地址的值。拿到该引用就相当 于得到了该内存处的对象。因此效率降低但是安全性增加,以及稍微降低了一点学习成本。
然后说回这个算法题本身,其实就是如果l1比l2小,就让l2与l1.next比较;如果l1>=l2就让l2.next和l1比较。这道题其实也有一点点递归的感觉,因为只针对了一个链表的一个节点。
最大子序和
- 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。*
class Solution {
public int maxSubArray(int[] nums) {
// // 首先数组长度为1则直接返回
int n = nums.length;
if(n==0){
return 0;
}
int[] dp = new int[n];
dp[0] =nums[0];
for(int i = 1; i<n; ++i){
dp[i] = Math.max(nums[i],dp[i-1]+nums[i]);
}
int res = Integer.MIN_VALUE;
for(int j = 0;j<dp.length;++j){
res = Math.max(res,dp[j]);
}
return res;
}
}
为啥我觉得基本上每一题都用到了递归的思想。。。这一题中心思想就是,如果某个数比前面所有的加起来都大,那它就是最大子序和。然后需要注意java当中的一个函数 Math.max,寻找最大值,这个应该很多算法题都会用到
然后这里我new的数组就是局部变零,好像也没有这个问题,所以其实我不太明白斐波那契数列里面为什么hashmap不能作为全局变量。
两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
class Solution {
int carry =0;
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 然后需要进位符,默认为0
// null 的情况
if ((l2 == null) && (l1 == null) && (carry == 0)) {
return null;
} else if ((l2 != null) && (l1 == null) && carry == 0) {
return l2;
} else if ((l1 != null) && (l2 == null) && (carry == 0)) {
return l1;
}
//接下来就是正常情况了
// sum为两个节点和进位符相加
int sum = (l1==null?0:l1.val)+(l2==null?0:l2.val)+carry;
carry = sum/10;
int value=sum%10;
ListNode node = new ListNode(value);
node.next =addTwoNumbers((l1==null?null:l1.next),(l2==null?null:l2.next));
return node;
}
}
中心思想是要new一个int来储存进位符。以及这个存储的ListNode也是先new出来的不是像上一题那样直接指指指;感觉这个让我来写我可能写不出来。。。
还有就是除法和取余运算,除法都是地板除,只取整数部分
递归体现在addTwoNumbers ,但我没想到的是在方法里面也可以有三元运算符
汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离*
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x^y);
}
}
首先要理解异或:相同结果为0,不同为1,所以只需要使用bitCount统计已获之后数值在二进制下“1”的数量。
滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int head = 0;
int tail = k - 1;
List<Integer> result = new ArrayList<>();
for (int i = k - 1; i < nums.length; i++) {
// 在里面挑出head和target中间的部分
int[] slide = Arrays.copyOfRange(nums, head, tail + 1);
// 找到最大值
if (Arrays.stream(slide).max().isPresent()) {
int max = Arrays.stream(slide).max().getAsInt();
result.add(max);
}
// 将最大值添加到list
//再把list转化为int[]
if (tail < (nums.length - 1)) {
head++;
tail++;
}
}
return result.stream().mapToInt(Integer::valueOf).toArray();
}
}
第一次自己做出来!快乐!
虽然运行时间高然后占用内存也很多,但我不管我就是最棒的!!!!!!
反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
class Solution {
public ListNode reverseList(ListNode head) {
//申请节点,pre和 cur,pre指向null
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
//记录当前节点的下一个节点
tmp = cur.next;
//然后将当前节点指向pre
cur.next = pre;
//pre和cur节点都前进一位
pre = cur;
cur = tmp;
}
return pre;
}
class Solution {
public ListNode reverseList(ListNode head) {
//递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
//这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
head.next.next = head;
//防止链表循环,需要将head.next设置为空
head.next = null;
//每层递归函数都返回cur,也就是最后一个节点
return cur;
}
}
非递归好理解很多
递归就是不要在脑海里模拟,我这小脑袋压不了几个栈的哈哈哈哈哈
斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。*
class Solution {
Map<Integer, Integer> cache = new HashMap<>();
public int fib(int n) {
if (n == 0 || n == 1) {
return n;
}
if (cache.containsKey(n)) {
return cache.get(n);
}
int first = (fib(n - 1)) % 1000000007;
int second = (fib(n - 2)) % 1000000007;
int ret = (first + second) % 1000000007;
cache.put(n, ret);
return ret;
}
}

浙公网安备 33010602011771号