Something learned from leetcode (3)

41.Merge有序单链表
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
递归解法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 ==NULL)
        return l2;
        if(l2 ==NULL)
        return l1;

        if(l1->val <= l2->val)
        {
            l1->next=mergeTwoLists(l1->next,l2);
            return l1;
        }

        else
        l2->next=mergeTwoLists(l2->next,l1);
        return l2;

    }

非递归解法:
设置一个head节点,省去单独处理原来的头结点。
head
list1: 1–>3–>6
list2: 2–>5
l1 l2谁小把谁挂到新链表上

    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    ListNode head(INT_MIN);
        ListNode *p= &head;

        while(l1 && l2)
        {
            if(l1->val < l2->val)
             {
                 p->next=l1;
                 l1=l1->next;
             }

            else
            {
             p->next=l2;
             l2=l2->next;
            }

            p=p->next;

        }

        if(l1)
        p->next=l1;
        else
        p->next=l2;


        return head.next;   //head是一个节点,而不是指针。注意取next的方式
    }

from 21. Merge Two Sorted Lists
42.链表是否有环
快慢指针的典型应用有两种
1.检测是否成环
快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2,慢指针每次向前移动1次。
让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置;如果快指针到达NULL,说明链表以NULL为结尾,不是循环链表。如果 快指针追上慢指针,则表示出现了循环。
2.在有序链表中寻找中位数
快指针的移动速度是慢指针移动速度的2倍,因此当快指针到达链表尾时,慢指针到达中点。程序还要考虑链表结点个数的奇偶数因素。

bool hasCycle(ListNode *head) {

        if(head==NULL || head->next ==NULL)
        return false;

        ListNode* fast=head;
        ListNode* slow=head;

        fast=fast->next->next;
        slow=slow->next;

        while(true)
        {

            if(fast ==NULL || fast->next ==NULL)
            return false;

            if(fast ==slow || fast->next==slow)
            return true;

            fast=fast->next->next;
        slow=slow->next;

        }

    }

from 141. Linked List Cycle
43.找单链表环起点
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
基于上题快慢指针找环,fast与slow相遇(fast==slow)时停止。将其中一个指向起点,两个指针每次同时向前移动一步,再次相遇时即为环起点。

    ListNode *detectCycle(ListNode *head) {

        if(head==NULL || head->next ==NULL)
        return NULL;

        ListNode* fast=head;
        ListNode* slow=head;

        fast=fast->next->next;
        slow=slow->next;

        while(true)
        {
            if(fast==NULL || fast->next ==NULL)
            return NULL;


            if(fast ==slow)  //区别与找环时的fast可以追上slow,此处一定要相遇
            break;

        fast=fast->next->next;
        slow=slow->next;

        }

        slow=head;


        while(slow !=fast){
            slow=slow->next;
            fast=fast->next;
        }

        return slow;


    }

from 142. Linked List Cycle II
44.二叉树路径
Path sum I: 是否存在根到叶子的路径使得加和等于指定值。
递归完成

bool hasPathSum(TreeNode* root, int sum) {
        if(root==NULL)
        return false;

    else if( root->left ==NULL && root->right==NULL && root->val==sum)
        return true;


    else return hasPathSum(root->left, sum-(root->val)) || hasPathSum(root->right, sum-(root->val));
    }

PathSum II : 给出I中符合条件的路径 如 {[1,2,3],[1,3,2]}

vector<vector<int>> pathSum(TreeNode* root, int sum) {
     vector <int> temp;
        vector<vector<int>> res;
        doPathSum(root,sum,res,temp);


        return res;
    }
//----------------------
void doPathSum(TreeNode *root, int sum,  vector<vector<int>> &res, vector<int> temp)
    {
        if(!root)
            return;

        temp.push_back(root->val);


        if(root->left ==NULL && root->right==NULL && root->val==sum)
            res.push_back(temp);

       doPathSum(root->left,sum-root->val,res,temp);

        doPathSum(root->right,sum-root->val,res,temp);

       temp.pop_back();  //删除vector最后的元素


    }

Path Sum III, 不用满足root到leaf, 是否存在向下的路径和等于指定值

int pathSum(TreeNode* root, int sum) {
        int res=0;


        if(root==NULL)
            return 0;

        dfs(root,sum,res);

        return res;
    }

    void doPathSum(TreeNode *root,int sum, int &res)
    {

        if(!root)
            return;
       // temp.push_back(root->val);

        if(root->val==sum)
        {res++;

        }
        doPathSum(root->left,sum-(root->val),res);

        doPathSum(root->right,sum-(root->val),res);

     //   temp.pop_back();

    }



    void dfs(TreeNode* root,int sum,int &res){
        if(root==NULL)
            return;
        stack<TreeNode*> s;
        s.push(root);
        TreeNode *p=s.top();
        while(!s.empty())
        {
            p=s.top();
            doPathSum(p,sum,res);
            s.pop();
            if(p->right)
            s.push(p->right);
            if(p->left)
            s.push(p->left);
        }


    }

from 112. Path Sum,113.Path Sum II, 437.Path Sum III
45. k sum问题

(1) 2 sum问题
数组中找出两个元素,和等于sum。要求给出所有结果(不重复)。
编程之美双指针法:
前提:数组有序,排序
若nums[head]+nums[tail]== sum, 保存结果且head++, tail--
若nums[head]+nums[tail]<sum,head++ 
若nums[head]+nums[tail]>sum, tail--
此时结果有重复,需要去重。两种去重技巧set唯一性和vector unique。

(2)3 sum问题转化为2 sum解决,排序后对于nums[0],只需在后边找到2 sum等于-nums[0]的结果。依次处理 nums[1]... nums[n]。
**重要:** a b c d e 中,后边的元素不用考虑之前的元素
例如 对于b,只需考虑 c d e中的 2 sum,因为 ac ad  ae在 a2 sum中已经考虑。
vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> res;

        if(nums.size()<3)
            return vector<vector<int>>(res.begin(),res.end());
        sort(nums.begin(),nums.end());

    threeeTo2Sum(res,nums,0);
//    sort(res.begin(),res.end());
//vector<vector<int >>::iterator  //iter=unique(res.begin(),res.end());  
//unique把vector中重复元素放入vector结尾,并返回一个迭代器指向重复元素开始处。输入3 2 1 1 2 3,它的输出是1 2 3 2 3 3
//    res.erase(iter,res.end());  删除重复元素
        vector<vector<int>> ok(res.begin(),res.end());
        return ok;
    }
void threeeTo2Sum(set<vector<int>> &res, vector<int> &nums,int sum)
{
    for(int i=0;i<nums.size()-2;i++)
    {
        int top=i+1,tail=nums.size()-1;
        while(top<tail)
        {
            if(nums[top]+nums[tail]+nums[i]==sum)
            {
                vector <int> temp;
                temp.push_back(nums[i]);
                temp.push_back(nums[top]);
                temp.push_back(nums[tail]);
                res.insert(temp);
                top++;
                tail--;
                continue;
            }

            if(nums[top]+nums[tail]+nums[i]<sum)
                top++;
            else
                tail--;

        }
    }
}

去重代价非常高,leetcode ac时间超过500ms,使用另一种方法过滤重复对象:

vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;

        if(nums.size()<3)
            return vector<vector<int>>(res.begin(),res.end());
        sort(nums.begin(),nums.end());

    threeTo2Sum(res,nums,0);


        return res;
    }

   void threeTo2Sum(vector<vector<int>> &res, vector<int> &nums,int sum){
    for(int i=0;i<nums.size()-2;)
    {
        int top = i+1, tail = nums.size()-1;
            while(top<tail)
            {
                if(nums[i]+nums[top]+nums[tail]==sum)
                {
                    res.push_back({nums[i], nums[top], nums[tail]});
                    top++;
                    tail--;
                    while ((top < tail) && nums[top] == nums[top - 1]) top++;
                    while ((top < tail) && nums[tail] == nums[tail + 1]) tail--;
                }



               else if(nums[i]+nums[top]+nums[tail]<sum)
                {  //坑,调了两小时。一定要else if,若使用if,等于sum的情况会继续执行最下边的else语句块。
                    top++;
                    while(  (top<tail) && nums[top]==nums[top-1]) top++;
                }


                else
                {
                    tail--;
                    while(  (top<tail) && nums[tail]==nums[tail+1]) tail--;

                }
            }

        i++;
        while((i < nums.size()) && nums[i]==nums[i-1]) i++;



    }

}

from 15. 3Sum

46.单链表删除倒数第N个节点
输入:list: 1->2->3->4->5, and n = 2
输出:1->2->3->5
双指针一次遍历
1.pre向前移动n次,cur不动
2.若此时pre指向结尾空指针,则要删除的是头结点,返回head->next
3.同时向前移动pre和cur,pre指向最后一个节点时停止,此时cur指向要删除节点的上一个节点

    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* pre=head;
        ListNode* cur=head;

        for(int i=0;i<n;i++)
        {
            pre=pre->next;
        }

        if(pre==NULL)
            return head->next;
        while( (pre->next) != NULL)
        {
            pre=pre->next;
            cur=cur->next;
        }
        cur->next=cur->next->next;

        return head;

    }

from 19. Remove Nth Node From End of List
47.括号匹配
遇到左括号入栈 ,遇到右括号 有两种情况:1. 此时栈空,返回不匹配 2. 栈顶元素是否是其对应的左括号,不是则不匹配。
栈顶元素必须把来的右括号消掉。

bool isValid(string s) {
        stack<char> a;
    if(s.size())
    {
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='(' || s[i]=='[' || s[i]=='{')
            a.push(s[i]);

            if(s[i]==')')
            {
                if(a.empty())
                    return false;
               else if(a.top()=='(')
                    a.pop();
                else
                    return false;
            }

            if(s[i]==']')
            {
                if(a.empty())
                    return false;
                else if(a.top()=='[')
                    a.pop();
                else
                    return false;
            }

            if(s[i]=='}')
            {
                if(a.empty())
                    return false;
              else  if(a.top()=='{')
                    a.pop();
                else
                    return false;
            }



        }
        if(a.empty())
        return true;
        else
            return false;

    }
           return true; 

    }

from 20. Valid Parentheses
48.最大连续子序列乘积
[2,3,-2,4]中 连续子序列[2,3]乘积最大
一次遍历做法: 访问到该节点时,以该节点为结尾的子序列乘积最大值 要么是该点本身,要么是该点乘以上一个节点的子序列最大值。 考虑负负得正,可能存在该点为负数,乘以上一个点最小值(负数)得到最大的可能,故还应保存每个点的乘积最小值。

int maxProduct(vector<int>& nums) {
            if(nums.size()==0)
            return 0;
        int maxArr[nums.size()]={nums[0]};
        int minArr[nums.size()]={nums[0]};
        int res=maxArr[0];


        for(int i=1;i<nums.size();i++)
        {
            maxArr[i]=max(nums[i],max(nums[i]*maxArr[i-1],nums[i]*minArr[i-1]));
            minArr[i]=min(nums[i],min(nums[i]*maxArr[i-1],nums[i]*minArr[i-1]));

            if(maxArr[i]>res)
            res=maxArr[i];
        }

        return res; 

    }

此思路同样可以应用与求最大子序列和(53. Maximum Subarray)
from 152. Maximum Product Subarray
49.背包问题(01)-动态规划
n个物品,从最后一个n-1开始考虑一直到0
(dp[i][j]表示当容量为j时,0-i号物品背包问题的最大价值)
若n-1能放进背包,该问题转化为子问题dp[n-1][j]=max(dp[n-1-1][j],dp[n-1-1][j-weight[n-1]]+value[n-1]) ,分别对应n-1号物品放不放进背包,比较他们的最大值。
边界条件为dp[0][j]的情况,此时能直接给出答案,若空间足够dp[0][j]=value[0],否则等于0。

使用一个二维数组存储中间结果,防止重复计算。

#include <iostream>
using namespace std;
int weight[] = {2,3,1,4,6,5};  
int value[] =  {5,6,5,1,19,7};
int capacity = 10;

int dp[500][500];//保存中间结果
//二维数组传参时不同于一维数组,形参中要指定数组范围
//如int zeroOnePack(int i,int j,int dp[500][500]){...}

int zeroOnePack(int i,int j){
    if(j<0)
    return 0;
    if(dp[i][j]!=-1)
    {return dp[i][j];}

    if(i==0)
    {
        if(weight[i]<=j)
        return value[i];
        else
        return 0;
    }
    if(j>=weight[i])
    {
    dp[i][j]=max(zeroOnePack(i-1,j),zeroOnePack(i-1,j-weight[i])+value[i]);
    return dp[i][j];
    }

    return zeroOnePack(i-1,j);
}
int main(){
for(auto&i:dp) //二维数组遍历,使用引用可修改数组的值
for(auto&j:i)   
j=-1;
cout<<zeroOnePack(sizeof(weight)/sizeof(int)-1,capacity)<<endl;;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值