刷题指南-0

第一题:两数相加(简单题,主要是为了复习链表)

(不会吧不会吧,真的有人这题都能提交错吗,哦,原来是我阿,那没事了)



题目描述:

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。



示例图:


测试用例:



问题分析:主要是模仿大数相加,用链表的方式来模拟计算机做加法运算。

可能考虑出现解决该问题:链表长度不一样,我们需要先遍历完较小的链表加和,再计算进位,再对较长的链表进行剩余的遍历,我们设置一个指针用来遍历,一个头节点用来返回整个链表。

(这里手画图)

C++(很不熟悉,基本都用python了,但是博士申请得上机选定用c++,只能硬磕)

代码如下:

/**

* Definition for singly-linked list.

* struct ListNode {

*    int val;

*    ListNode *next;

*    ListNode() : val(0), next(nullptr) {}

*    ListNode(int x) : val(x), next(nullptr) {}

*    ListNode(int x, ListNode *next) : val(x), next(next) {}

* };

*/classSolution{public:

    ListNode*addTwoNumbers(ListNode* l1, ListNode* l2){

        //从这里看起

        ListNodejg(0);//创建一个头节点

        ListNode *l3 = &jg;//创建一个遍历指针

        int flag; //设置进位

        flag=0;//初始化进位

        while(l1 !=nullptr && l2 !=nullptr) //如果俩链表都不为空

        {

            int s=0;

            s=l1->val+l2->val+flag; //求和并累加进位

            cout<val<

            cout<val<

            cout<<"这里是打印每俩个数的加和"<

            cout<

            cout<<"结束"<

            flag=s>=10? 1:0;

            l3->next=new ListNode(s%10);

            cout<<"这里是打印对于每一个链表的下一个数"<

            cout<next->val<

            cout<<"这里是打印对于每一个链表的下一个数"<

            l1=l1->next;

            l2=l2->next;

            l3=l3->next;

            //cout<val<

        while(l1 !=nullptr) //已经循环完最小的链表了,我们需要看看剩下的链表在哪里,如果在l1里,则加完它。

        {

            int s=0;

            s=l1->val+flag;

            l3->next=new ListNode(s%10);

            flag=s>=10?1:0;

            l1=l1->next;

            l3=l3->next;

        }

        while(l2 !=nullptr)//已经循环完最小的链表了,我们需要看看剩下的链表在哪里,如果在l2则加完它。

        {

            int s=0;

            s=l2->val+flag;

            l3->next=new ListNode(s%10);

            flag=s>=10?1:0;

            l2=l2->next;

            l3=l3->next;

        }

        if(flag) //如果加到最后,还有一个进位怎么办,我们还需要创建一个节点来保存它

        {

            l3->next=new ListNode(flag);

        }

        return jg.next;//返回头节点的next

    }

};



第二题 寻找两个正序数组的中位数

暴力合并求中值很简单,但是光看题解的方法我就看了一个小时才会(我好菜)



题目描述:给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

测试用例:



首先看非进阶的方式,合并排序,没有什么好说的,给出实现方法。因为这个题目它不会出现重复值,所以我们也没有必要去重。

python 实现:

class Solution:

    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:        

        def findMid(num):

            if len(num) % 2 == 0: //如果是偶数数组

                return (num[int(len(num)/2)] + num[int((len(num))/2-1)]) / 2  //取中间俩数求和

            else :

                return num[int((len(num)-1)/2)] * 2 / 2  //取中间一个数

        num = sorted(nums1 + nums2) //数据相加排序就可以了

        return findMid(num) //返回结果

python的好处在于可以直接相加 nums1 + nums2 就可以合并数组,如果是C++,则需要我们开一个大数组,设置俩标记符i和j,从i=0和j=0开始,每一次比较俩数的大小,小的数就放进新数组,并且小的数组的下标++。

简单的方法直接说完到难的方法



二分方法(听说是408真题??)

可以看题解一大堆文字哔哔哔,但是基本还是摸不着头脑,但是可以这样理解。

首先我们有俩个有顺序的数组(从小到大)

分别为N1=[1,2,3,4]

和N2=[3,5,7,9,10]

我们可以使用一个小技巧

我们分别找第 (m+n+1) / 2 个元素,和 (m+n+2) / 2 个,然后求其平均值即可,这对奇偶数组均适用。

其中m为N1的长度,n为N2的长度,分别为4和5

我们来验证一下,对于N1和N2的合并数组N3=[1,2,3,3,4,5,7,9,10]

 (m+n+1) / 2=5    (m+n+2) / 2=int(5.5)=5

对于奇数组中位数第五个,验证无问题(你非要我数学证明那我就没办法了qaq)

下一步,我们可以通过找第k小数的方法来解决它,对于上面的例子,我们用找第5小数来计算,

//找第k小数

 public int find(int[] nums1, int i, int[] nums2, int j, int k){

        if( i >= nums1.length) return nums2[j + k - 1];//i是nums1的起始标识,如果过界了,我们需要从nums2里找中位数,直接取j + k - 1,为什么?我也不知道大佬解惑

        if( j >= nums2.length) return nums1[i + k - 1];//j是nums1的起始标识,如果过界了,我们需要从nums1里找中位数,直接取i + k - 1,为什么?我也不知道大佬解惑

        //k为1时,指的是迭代到边界了,我们直接返回俩数值最小值

        if(k == 1){

            return Math.min(nums1[i], nums2[j]);

        }

        int midVal1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : 10000000;

        int midVal2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : 10000000;

        if(midVal1 < midVal2){

            return find(nums1, i + k / 2, nums2, j , k - k / 2);

        }else{

            return find(nums1, i, nums2, j + k / 2 , k - k / 2);

        }        

    }

上面代码的思路如果同学(菜鸡申请py交易)们想理解的话可以这么理解,每次都会计算俩个数组的中间值,然后把中间值对比,如果中间值小的数组的中间值前一半的部分就会丢弃(通过什么方式丢弃 i+k/2或者j+k/2),然后剩下的接着比,一直到i或j越界为止。或者k==1为止。

对我来说,记住findkth()函数就很好了,再理解原理(相当于走了一条捷径)

(但是总所周知的是,所有捷径都有代价,读书如此,做人亦是如此)

此题结束。



第三题 最长回文子串(动态规划,Manacher 算法)

中等难度题目

其实主要动态规划做的不够多,不知道怎么去匹配问题。

这里先偷一张图,看看大佬的动态规划思路是什么样的

大佬链接在这里

https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/


问题分析:dp[i][j] 表示子串 s[i..j] 是否为回文子串,这里子串 s[i..j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。

dp二维数组里一共有俩个值,分别为true和false,分别代表s[i..j]是否为回文数。

分类思考

(1)假如s[i..j]的长度只有1,那它肯定是回文数

(3)假如s[i..j]的长度有大于等于2(>=2),如果dp[i+1][j-1]是回文数,并且,注意是并且,对于dp[i+1][j-1]新加入的数s[i]==s[j],那么dp[i][j]是true,s[i..j]是回文数。

下面开始填表(没有时间画图,我就只能偷了(什么,读书人的事情不算偷,那没事了))

下面直接给其他人代码了。(我会自己补上的,qaq)(主要是太菜了,光看理论就看了挺久)

class Solution:

    def longestPalindrome(self, s: str) -> str:

        size = len(s) #获取s的值,没有什么问题

        if size < 2:

            return s #如果小于2,肯定是回文数,我们直接返回就可以了。

        dp = [[False for _ in range(size)] for _ in range(size)] #构建二维数组,附上初值

        max_len = 1 //记录最大长度

        start = 0

        for i in range(size): //初始化

            dp[i][i] = True  //因为每一个s[i][i]的长度都是1,所以都是回文数

        for j in range(1, size)://第一个循环

            for i in range(0, j): //第二个循环

                if s[i] == s[j]:

                    if j - i < 3:

                        dp[i][j] = True

                    else:

                        dp[i][j] = dp[i + 1][j - 1]

                else:

                    dp[i][j] = False

                if dp[i][j]: //如果dp[i][j]==true

                    cur_len = j - i + 1 //每次更新就更新一下最大长度

                    if cur_len > max_len:

                        max_len = cur_len 

                        start = i   //更新start的起始位置

        return s[start:start + max_len] //返回回文串

第一个方法结束。



第二个方法Manacher算法

从每个点出发,向俩边寻找对比,最后p里最大的值就是我们要的,最大值位置对应的坐标为回文中心。

class Solution:

    # Manacher 算法

    def longestPalindrome(self, s: str) -> str:

        # 特判

        size = len(s)

        if size < 2:

            return s

        # 得到预处理字符串

        t = "#"

        for i in range(size):

            t += s[i]

            t += "#"

        # 新字符串的长度

        t_len = 2 * size + 1

        # 当前遍历的中心最大扩散步数,其值等于原始字符串的最长回文子串的长度

        max_len = 1

        # 原始字符串的最长回文子串的起始位置,与 max_len 必须同时更新

        start = 0

        for i in range(t_len):

            cur_len = self.__center_spread(t, i)

            if cur_len > max_len:

                max_len = cur_len

                start = (i - max_len) // 2

        return s[start: start + max_len]

    def __center_spread(self, s, center):

        size = len(s)

        i = center - 1

        j = center + 1

        step = 0

        while i >= 0 and j < size and s[i] == s[j]:

            i -= 1

            j += 1

            step += 1

        return step

你可能感兴趣的:(刷题指南-0)