牛客题霸-算法篇

人生不像做饭,不能等万事俱备了才下锅

跳台阶

题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

class Solution {
public://循环
    int jumpFloor(int number) {
        int pos[number];//possibility
        pos[0] = 1;
        pos[1] = 2;
        for (int i = 2; i < number; i++)
            pos[i] = pos[i-1] + pos[i-2];
        return pos[number-1];
    }
};
class Solution {
public://迭代
    int jumpFloor(int number) {
        return jump(number);
    }
    int jump(int number)
    {
        if (number == 1)
            return 1;
        if (number == 2)
            return 2;
        return jump(number - 1) + jump(number - 2);
    }
};

用两个栈实现队列

题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

class Solution
{
public:
    void push(int node) {
        while (!stack1.empty())
        {
            stack2.push(stack1.top());
            stack1.pop();
        }
        stack1.push(node);
        while (!stack2.empty())
        {
            stack1.push(stack2.top());
            stack2.pop();
        }
    }
    int pop() {
        int node = stack1.top();
        stack1.pop();
        return node;
    }
private:
    stack<int> stack1;
    stack<int> stack2;
};

子数组的最大累加问题

题目:给定一个数组arr,返回子数组的最大累加和 例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12. 题目保证没有全为负数的数据
[要求] 时间复杂度为O(n),空间复杂度为O(1)

class Solution {
public:
    int maxsumofSubarray(vector<int>& arr) {
        int length = arr.size();
        int total = 0, biggest = 0;
        for (int i = 0; i < length; i++)
        {
            if (total <= 0)
                total = arr[i];
            else
                total += arr[i];
            if (total > biggest)
                biggest = total;
        }
        return biggest;
    }
};

字符串的最长无重复子字符串(剑指Offer 面试题48)

题目:给定一个数组arr,返回arr的最长无的重复子串的长度(无重复指的是所有数字都不相同)。

#include <unordered_map>
class Solution {
public:
    int maxLength(vector<int>& arr) {
        unordered_map<int, int> nums;
        int length = 0, maxLength = 0;
        for (int i = 0; i < arr.size(); i++)
        {
            if (nums.find(arr[i]) == nums.end())
                length++;//如果该字符没有出现过则长度+1
            else//如果出现过则考虑两种情况
            {
                int dis = i - nums.find(arr[i])->second;
                if (dis > length)
                    length++;//该字符上次出现是在最长子串之前
                else//该字符在当前最长子串之中
                {
                    if (length > maxLength)
                        maxLength = length;//保存当前的最长字串长度
                    length = dis;//将该字符两次出现中间的字符串作为新的最长字串
                }   
            }
            nums[arr[i]] = i;
        }
        if (length > maxLength)//注意最后一次的最长字串
            maxLength = length;
        return maxLength;
    }
};

合并有序链表

题目:将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的,且合并后新链表依然有序。
输入:{1},{2}
输出:{1,2}

class Solution {
public://迭代
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
    {
        return merge(l1, l2);
    }
    ListNode* merge(ListNode* l1, ListNode* l2)
    {
        if (l1== nullptr)
            return l2;
        if (l2 == nullptr)
            return l1;
        ListNode* mergedNode = nullptr;
        if (l1->val < l2->val)
        {
            mergedNode = l1;
            mergedNode->next = merge(mergedNode->next, l2);//此时后面的已全部排序完毕,将选出的结点与之后的结点相连
        }
        else
        {
            mergedNode = l2;
            mergedNode->next = merge(l1, mergedNode->next);
        }
        return mergedNode;
    }
};
class Solution {
public://循环
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr)
            return l2;
        if (l2 == nullptr)
            return l1;
        ListNode* node = new ListNode(0), *result = node, *node1 = l1, *node2 = l2;
        while (node1 != nullptr && node2 != nullptr)
        {
            if (node1->val < node2->val)
            {
                node->next = node1;
                node = node1;
                node1 = node1->next;
            }
            else
            {
                node->next = node2;
                node = node2;
                node2= node2->next;
            }
        }
        if (node1 == nullptr)//注意未遍历完的结点
            node->next = node2;
        else if (node2 == nullptr)
            node->next = node1;
        return result->next;
    }
};

寻找第K大

题目:有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
输入:[1,3,5,2,2],5,3
输出:2

class Solution {
public://注意是第K大,也就是从大到小排序,注意边界条件
    int findKth(vector<int> a, int n, int K) {
        find(a, 0, n-1, K-1);
        return a[K-1];
    }
    void find(vector<int>& a, int begin, int end, int K)
    {
        int index = Partition(a, begin, end);
        int begin_1 = begin, end_1 = end;
        while (K != index)
        {
            if (K > index)
            {
                begin_1 = index;
                index = Partition(a, index+1, end_1);
            }
            else if (K < index)
            {
                end_1 = index;
                index = Partition(a, begin_1, index-1);
            }
        }
    }
    int Partition(vector<int>& a, int begin, int end)
    {
        int value = a[begin];
        while (begin < end)
        {
            while (a[end] <= value && begin < end)
                end--;
            a[begin] = a[end];
            while (a[begin] >= value && begin < end)
                begin++;
            a[end] = a[begin];
        }
        a[end] = value;
        return end;
    }
};

合并两个有序的数组

题目:给出两个有序的整数数组A和B,请将数组B合并到数组A中,变成一个有序的数组
注意:可以假设A数组有足够的空间存放B数组的元素,A和B中初始的元素数目分别为m和n

class Solution {
public:
    void merge(int A[], int m, int B[], int n) {
        int p = m + n - 1;
        n -= 1;
        m -= 1;
        while (m >= 0 && n >= 0)
        {
            if (A[m] > B[n])
                A[p--] = A[m--];
            else
                A[p--] = B[n--];
        }
        if (m < 0)
            while (n >= 0)
                A[p--] = B[n--];
    }
};

删除链表的倒数第 n 个结点

题目: 给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针 例如,给出的链表为:1→2→3→4→5,n = 2 删除了链表的倒数第 n 个节点之后,链表变为1→2→3→5
备注:题目保证 n 一定是有效的,请给出请给出时间复杂度为O(n)的算法
思路:设置两个指针,同时指向头结点,快的先走n步,当块的指针指向空指针时慢的指针正好指向倒数第n个结点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if (head == nullptr)
            return nullptr;
        ListNode *low = head, *fast = head;
        while (n-- >= 0)//此处的条件为>=0,也就是慢的指针会留在倒数第n+1个结点
        {
            if (fast == nullptr)//需要考虑删除的是头结点的情况,此时直接返回头结点的下一结点
                return head->next;
            fast = fast->next;
        }
        while (fast != nullptr)
        {
            fast = fast->next;
            low = low->next;
        }
        low->next = low->next->next;
        return head;
    }
};

大数加法

题目:以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。(字符串长度不大于100000,保证字符串仅由’0’~'9’这10种字符组成)
例如:输入:“1”,“99” ,输出:“100”

class Solution {
public:
    string solve(string s, string t) {
        if (s.length() == 0)
            return t;
        else if (t.length() == 0)
            return s;
        int m = s.size()-1, n = t.size()-1, total = 0;
        string result;
        while (m >= 0 || n >= 0 || total)
        {
            if (m >= 0)
                total += s[m--] - '0';
            if (n >= 0)
                total += t[n--] - '0';
            result = to_string(total%10) + result;
            total /= 10;
        }
        return result;
    }
};

最长公共子串

题目:给定两个字符串str1和str2,输出两个字符串的最长公共子串,题目保证str1和str2的最长公共子串存在且唯一。
例如:输入:“1AB2345CD”,“12345EF” ,输出:“2345”
思路:最直接的方法,遍历寻找最长公共子串,代码如下:

class Solution {
public:
    string LCS(string str1, string str2) {
        if (str1.size() == 0 || str2.size() == 0)
            return "";
        if (str1 == str2)
            return str1;
        if (str1.size() > str2.size())
            swap(str1, str2);
        int length1 = str1.size(), length2 = str2.size();
        string str, result;
        for (int i = 0; i < length1; i++)
        {
            int j = 0, m = i;
            while (j < length2 && m < length1)
            {
                if (str1[m] == str2[j])
                { 
                    str += str2[j];
                    j++;
                    m++;
                }
                else
                {
                    if (str.size() > result.size())
                        result = str;
                    str.clear();
                    j++;
                    m = i;
                }
            }
        }
        return result;
    }
};

思路:动态规划,与序列比对算法Smith-Waterman算法类似,构建打分矩阵,然后回溯,代码如下:

class Solution {
public:
    string LCS(string str1, string str2) {
        if (str1.size() == 0 || str2.size() == 0)
            return "";
        if (str1 == str2)
            return str1;
        int len1 = str1.size() + 1, len2 = str2.size() + 1;
        int* matrix = new int[len1 * len2]{ 0 };
        int maxLength = 0, record[2] = { 0 };
        for (int i = 1; i < len1; i++)
        {
            for (int j = 1; j < len2; j++)
            {
                if (str1[i - 1] == str2[j - 1])
                {
                    matrix[i * len2 + j] = matrix[(i - 1) * len2 + j - 1] + 1;
                    if (matrix[i * len2 + j] > maxLength)
                    {
                        maxLength = matrix[i * len2 + j];
                        record[0] = i - 1;
                        record[1] = j - 1;
                    }
                }
            }
        }
        int m = record[0], n = record[1];
        string result;
        while (str1[m] == str2[n])
        {
            result = str1[m] + result;;
            m--;
            n--;
        }
        return result;
    }
};

两个链表的第一个公共结点

题目:输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
思路:遍历第一个链表,对每个结点遍历第二个链表,直至找到公共结点或者没有,时间复杂度O(n),代码略;另一种思路代码如下:
分析:两种情况,如果长度相同,那么直接找到共同结点,或者没有共同结点,同时为nullptr,此时while循环结束;或者长度不一样,当一个链表先走到末尾时,此时两个链表之间的距离就是二者的长度差,将指针指向另一个链表的头指针(更长的那一个,还没有走完),当另一个链表也走完时,将指针指向较短链表的头指针,此时相当于两个链表长度一致。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == nullptr || pHead2 == nullptr)
            return nullptr;
        ListNode *p1 = pHead1, *p2 =pHead2;
        while (p1 != p2)
        {
            p1 = (p1 == nullptr ? pHead2 : p1->next);
            p2 = (p2 == nullptr ? pHead1 : p2->next);
        }
        return p1;
    }
};

两个链表生成相加链表

题目:假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为1->0->0->0。也就是937+63=1000。
思路:遍历两个链表,将结点存入栈中,然后依次相加,采用头插法生成新链表。代码如下:

class Solution {
public:
    ListNode* addInList(ListNode* head1, ListNode* head2) {
        if (head1 == nullptr)
            return head2;
        if (head2 == nullptr)
            return head1;
        int state = 0, total = 0;
        stack<ListNode*> stack1, stack2;
        ListNode *node = nullptr, *nextNode = nullptr;
        while (head1 != nullptr)
        {
            stack1.push(head1);
            head1 = head1->next;
        }
        while (head2 != nullptr)
        {
            stack2.push(head2);
            head2 = head2->next;
        }
        ListNode* newHead = new ListNode(1);
        while (!stack1.empty() || !stack2.empty())
        {
            if (!stack1.empty())
            {
                total += stack1.top()->val;
                stack1.pop();
            }
            if (!stack2.empty())
            {
                total += stack2.top()->val;
                stack2.pop();
            }
            total += state;
            node = new ListNode(total%10);
            node->next = nextNode;
            newHead->next = node;
            nextNode = node;
            state = total / 10;
            total = 0;
        }
        return state == 1 ? newHead : newHead->next;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值