算法两周总结

两周前才开始刷算法,其实已经有点晚了,但种一棵树最好的时候是十年前其次就是现在;以及才刷了两周就能感觉到自己的进步了,虽然很菜但起码不是当初那种一看到题目就懵逼的状态了。好了话不多说开始总结

类型

两数之和

给定一个整数数组 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;

    }
}

成员变量存在于堆内存,局部变量存在于栈内存;虽然使用局部变量要遵循就近原则,但是这道题我用了递归的方法,递归用栈,所以估计是再把hashmap放到栈里面就会超时了。

posted @ 2021-06-20 19:09  concise_d  阅读(55)  评论(0)    收藏  举报