剑指offer C++

1 输入一个链表,反转链表后,输出新链表的表头。

//当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点

        //需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2

        //即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了

        //所以需要用到pre和next两个节点

        //1->2->3->4->5

        //1<-2<-3 4->5

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead == NULL) return NULL;
        ListNode* pre = NULL;
        ListNode* next = NULL;
        while(pHead != NULL)
        {
            next = pHead->next;
            pHead->next = pre;
            
            pre = pHead;
            pHead = next;
        }
        return pre;
    }
};

 2. 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL) return pHead2;
        else if(pHead2 == NULL) return pHead1;
        
        if(pHead1->val <= pHead2->val)
        {
            pHead1->next = Merge(pHead1->next, pHead2);
            return pHead1;
        }else{
            pHead2->next = Merge(pHead1, pHead2->next);
            return pHead2;
        }
    }
};

3.输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
         if(!pRoot1)
            return false;
        if(!pRoot2)
            return false;
        return  ( dfs(pRoot1,pRoot2)) || HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);
    }
    
    bool dfs(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(!pRoot2) return true;
        if(!pRoot1) return false;     //注意这两句的顺序,先判断pRoot2,再pRoot1

        if(pRoot1->val != pRoot2->val) return false;
        return (dfs(pRoot1->left, pRoot2->left) && dfs(pRoot1->right, pRoot2->right));
    }
};

4. 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

class Solution {
public:
    vector printMatrix(vector > matrix) {
        vectorres;
        res.clear();
        int row=matrix.size();//行数
        int collor=matrix[0].size();//列数
        //计算打印的圈数
        int circle=((row         for(int i=0;i             //从左向右打印
            for(int j=i;j                 res.push_back(matrix[i][j]);         
            //从上往下的每一列数据
            for(int k=i+1;k                 res.push_back(matrix[k][collor-1-i]);
            //判断是否会重复打印(从右向左的每行数据)
            for(int m=collor-i-2;(m>=i)&&(row-i-1!=i);m--)
                res.push_back(matrix[row-i-1][m]);
            //判断是否会重复打印(从下往上的每一列数据)
            for(int n=row-i-2;(n>i)&&(collor-i-1!=i);n--)
                res.push_back(matrix[n][i]);
        }
        return res;
    }
};

 

5. 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

每入栈一次,就与辅助栈顶比较大小,如果小就入栈,如果大就入栈当前的辅助栈顶

当出栈时,辅助栈也要出栈

这种做法可以保证辅助栈顶一定都当前栈的最小值

class Solution {
public:
    stack stack1,stack2;
    void push(int value) {
         stack1.push(value);
        if(stack2.empty())
            stack2.push(value);
        else if(value<=stack2.top())
        {
            stack2.push(value);
        }
    }
    void pop() {
        if(stack1.top()==stack2.top())
            stack2.pop();
        stack1.pop();
    }
    int top() {
        return stack1.top();  
    }
    int min() {
        return stack2.top();
    }
};

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。

在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。最后的验证 ,防止没有时输出最后一个数字(不满足条件,所以需要最后验证一下)

class Solution {
public:
    int MoreThanHalfNum_Solution(vector numbers) {
        int n = numbers.size();
        if (n == 0) return 0;
         
        int num = numbers[0], count = 1;
        for (int i = 1; i < n; i++) {
            if (numbers[i] == num) count++;
            else count--;
            if (count == 0) {
                num = numbers[i];
                count = 1;
            }
        }
        // Verifying
        count = 0;
        for (int i = 0; i < n; i++) {
            if (numbers[i] == num) count++;
        }
        if (count * 2 > n) return num;
        return 0;
    }
};

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

{

set和multiset会根据特定的排序原则将元素排序。两者不同之处在于,multisets允许元素重复,而set不允许重复。

}

最大堆排序nlog(K)

链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
来源:牛客网

利用堆排序,O(N logK),适合处理海量数据

(1) 遍历输入数组,将前k个数插入到推中;(利用multiset来做为堆的实现)

(2) 继续从输入数组中读入元素做为待插入整数,并将它与堆中最大值比较:如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,则抛弃这个数,继续读下一个数。

这样动态维护堆中这k个数,以保证它只储存输入数组中的前k个最小的数,最后输出堆即可。

class Solution {
public:
    vector GetLeastNumbers_Solution(vector input, int k) {
         vector result;
         int len = input.size();
         if(input.empty() || k<=0 || len < k) return result;
         
         multiset > leastNumbers; // 从大到小排序
         multiset >::iterator iterGreater;
        vector::iterator iter = input.begin();
        for(; iter != input.end(); ++iter)
        {
            // 将前k个数直接插入进multiset中,注意是小于K
            if(leastNumbers.size() < k)
            {
                leastNumbers.insert(*iter);
            }
            else
            {
                // 因为设置的从大到小排序,故multiset中第一个位置的元素即为最大值
                iterGreater = leastNumbers.begin();
                 
                // 如果input中当前元素比multiset中最大元素小,则替换;即保持multiset中这k个元素是最小的。
                if(*iter < *(leastNumbers.begin()))
                {
                    // 替换掉当前最大值
                    leastNumbers.erase(iterGreater);
                    leastNumbers.insert(*iter);
                }
            }
        }
        for(iterGreater = leastNumbers.begin();iterGreater!=leastNumbers.end();++iterGreater)
        {
            result.push_back(*iterGreater); // 将multiset中这k个元素输出
        }
         
        return result;
    }
};

https://www.cnblogs.com/ChinaHook/p/6985518.html

https://www.cnblogs.com/ChinaHook/p/6985444.html

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

使用动态规划

F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变

F(i)=max(F(i-1)+array[i] , array[i])

res:所有子数组的和的最大值

res=max(res,F(i))

 

如数组[6, -3, -2, 7, -15, 1, 2, 2]

初始状态:

    F(0)=6

    res=6

i=1:

    F(1)=max(F(0)-3,-3)=max(6-3,3)=3

    res=max(F(1),res)=max(3,6)=6

i=2:

    F(2)=max(F(1)-2,-2)=max(3-2,-2)=1

    res=max(F(2),res)=max(1,6)=6

i=3:

    F(3)=max(F(2)+7,7)=max(1+7,7)=8

    res=max(F(2),res)=max(8,6)=8

i=4:

    F(4)=max(F(3)-15,-15)=max(8-15,-15)=-7

    res=max(F(4),res)=max(-7,8)=8

以此类推

最终res的值为8

class Solution {
public:
    int FindGreatestSumOfSubArray(vector array) {
        if(array.empty()) return 0;
        int sum = array[0], tempsum = array[0]; //注意初始值 不能设为0 防止只有负数
        for(int i = 1; i < array.size(); i++) //从1开始 因为0的情况在初始化时完成了
        {
            tempsum = (tempsum < 0) ? array[i] : tempsum + array[i];
            sum = (tempsum > sum) ? tempsum : sum;
        }
        return sum;
    }
};

 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

回溯法

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        vector flags(rows*cols,0);
        bool condition=false;
        for(int i=0;i             for(int j=0;j             {
                if(isPath(matrix,flags,str,i,j,rows,cols) )
                    return true;
            }
        return false;  
    }
    
    bool isPath(char *matrix,vector flags,char* str,int x,int y,int rows, int cols)
    {
        if(x<0 || x>=rows || y<0 || y>=cols) //越界的点
            return false;     
 
        if( matrix[x*cols+y]== *str  &&  flags[x*cols+y]==0 )
        {
            flags[x*cols+y]=1;
 
            if(*(str+1)==0)  // 字符串结尾了(最后一个满足的)
                return true;
 
            bool condition =isPath(matrix,flags,(str+1),x,y-1,rows,cols) ||
                isPath(matrix,flags,(str+1),x-1,y,rows,cols)||
                isPath(matrix,flags,(str+1),x,y+1,rows,cols)||
                isPath(matrix,flags,(str+1),x+1,y,rows,cols);
            if(condition == false)
                flags[x*cols+y]=0;
            return condition;
        }
        else
            return false;
    }

};

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

回溯法

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        bool *flag = new bool[rows * cols];
        for(int i = 0; i < rows * cols; i++)
            flag[i] = false;
        int count=moving(threshold,rows,cols,0,0,flag);//从(0,0)坐标开始访问;
        delete[] flag;
        return count;
    }
    int moving(int threshold,int rows,int cols,int i,int j,bool* flag)
    {
        int count=0;
        if(check(threshold,rows,cols,i,j,flag))
        {
            flag[i*cols+j]=true;
            //标记访问过,这个标志flag不需要回溯,因为只要被访问过即可。
           //因为如果能访问,访问过会加1.不能访问,也会标记下访问过。
            count=1+moving(threshold,rows,cols,i-1,j,flag)
                   +moving(threshold,rows,cols,i,j-1,flag)
                   +moving(threshold,rows,cols,i+1,j,flag)
                   +moving(threshold,rows,cols,i,j+1,flag);
        }
        return count;
    }
    //检查当前位置是否可以访问
    bool check(int threshold,int rows,int cols,int i,int j,bool* flag)
    {
        if(i>=0 && i=0 && j             && getSum(i)+getSum(j)<=threshold
            && flag[i*cols+j]==false)
           return true;
        return false;
    }
    //计算位置的数值
    int getSum(int number)
    {
        int sum=0;
        while(number>0)
        {
            sum+=number%10;
            number/=10;
        }
        return sum;
    }
};

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

//deque s中存储的是num的下标

class Solution {

public:

    vector<int> maxInWindows(const vector<int>& num, unsigned int size)

    {

        vector<int> res;

        deque<int> s;

        for(unsigned int i=0;i

            while(s.size() && num[s.back()]<=num[i])//从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标

                s.pop_back();

            while(s.size() && i-s.front()+1>size)//当当前窗口移出队首元素所在的位置,即队首元素坐标对应的num不在窗口中,需要弹出

                s.pop_front();

            s.push_back(i);//把每次滑动的num下标加入队列

            if(size&&i+1>=size)//当滑动窗口首地址i大于等于size时才开始写入窗口最大值

                res.push_back(num[s.front()]);

        }

        return res;

    }

};

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

class Solution {
public:
    priority_queue, less > small;
    priority_queue, greater > big;
    void Insert(int num)
    {
        if(small.empty() || num<=small.top())
            small.push(num);
        else
            big.push(num);
        if(small.size() == big.size() +2)
        {
            big.push(small.top());
            small.pop();
        }
        if(small.size() +1 == big.size())
        {
            small.push(big.top());
            big.pop();
        }
    }

    double GetMedian()
    {
        return small.size() == big.size() ? (small.top() + big.top()) / 2.0 : small.top();
    }

};

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

//思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。

//     所以,按照中序遍历顺序找到第k个结点就是结果。

class Solution {
public:
    int count = 0;
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot)
        {
            TreeNode *ret = KthNode(pRoot->left, k);
            if(ret) return ret;
            if(++count == k) return pRoot;
            ret = KthNode(pRoot->right, k);
            if(ret) return ret;
        }
        return nullptr;
    }
};

请实现两个函数,分别用来序列化和反序列化二叉树

class Solution {
public:
    vector buf;
    void dfs1(TreeNode *root) {
        if(!root) buf.push_back(-1);  //-1=0xFFFFFFFF
        else {
            buf.push_back(root->val);
            dfs1(root->left);
            dfs1(root->right);
        }
    }
    TreeNode* dfs2(int* &p) {
        if(*p==0xFFFFFFFF) {
            p++;
            return NULL;
        }
        TreeNode* res=new TreeNode(*p);
        p++;
        res->left=dfs2(p);
        res->right=dfs2(p);
        return res;
    }
    char* Serialize(TreeNode *root) {    
        buf.clear();
        dfs1(root);
        int bufSize=buf.size();
        int *res=new int[bufSize];
        for(int i=0;i         return (char*)res;
    }
    TreeNode* Deserialize(char *str) {
        int *p=(int*)str;
        return dfs2(p);
    }
};

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

class Solution {
public:
        vector > Print(TreeNode* pRoot) {
            vector > vec;
            if(pRoot == NULL) return vec;
            
            queue q;
            q.push(pRoot);
            while(!q.empty())
            {
                int min = 0;
                int max = q.size();
                vector tmp;
                while(min                     TreeNode* cur = q.front();
                    q.pop();
                    tmp.push_back(cur->val);
                    if(cur->left) q.push(cur->left);
                    if(cur->right) q.push(cur->right);
                    min++;
                }
                vec.push_back(tmp);
            }
            return vec;
        }
};

 

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

class Solution {
public:
    vector > Print(TreeNode* pRoot) {
        vector > res;
        if(pRoot == NULL) return res;

        vector q;
        q.push_back(pRoot);
        bool model = true;
        while(!q.empty())
        {
            int min = 0;
            int max = q.size();
            vector tmp;
            vector row;
            tmp = q;
            q.clear();
            while(min                 TreeNode* cur = tmp.back();
                tmp.pop_back();
                row.push_back(cur->val);
                if(!model)
                {
                    if(cur->right) q.push_back(cur->right);
                    if(cur->left) q.push_back(cur->left);
                }else{
                    if(cur->left) q.push_back(cur->left);
                    if(cur->right) q.push_back(cur->right);
                }
                min++;
            }
            model = !model;
            res.push_back(row);
        }
        return res;
    }
};

 

 

你可能感兴趣的:(C++)