剑指offer

#二维数组查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

##题解:主要在从右上(或者左下)找,这样可以一次排除一行或者一列,而左上和右下不行

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if (array.empty()) return false;
        int size = array[0].size();
        int rows = array.size();
        int row = 0, column = size - 1;
        while (row < rows && column >= 0){
            if (array[row][column] == target) return true;
            else if (array[row][column] > target) column--;
            else row++;
        }
        return false;
    }
};

#替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

##题解:从结果出发,很简单的知道结果如何,考虑一个移动字符串比较少的方法。算好替换空格后的字符串长度,从尾部开始移动。(注意总的分配内存长)

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if (!str || length <=0) return;
        int n_blank = 0;
        char *p = str;
        while (*p != 0){
            if (*p == ' ') n_blank++;
            p++;
        }
        if (n_blank == 0) return;
        int len = strlen(str);
        int new_len = len + n_blank*2;
        if (new_len > length) return;
        char *p1 = str + len ;
        char *p2 = str + new_len ;
        while(n_blank > 0 && p2 > p1){
            if (*p1 == ' '){
                *p2 = '0';
                p2--;
                *p2 = '2';
                p2--;
                *p2 = '%';
                p2--;
                p1--;
                n_blank--;
            }
            else {
                *p2 = *p1;
                p1--;
                p2--;
            }
        }
	}
};

#从尾到头打印链表

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

##题解

先进后出,stack

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> v;
        stack<int> s;
        ListNode* p = head;
        while (p){
            s.push(p->val);
            p = p->next;
        }
        while (!s.empty()) {
            v.push_back(s.top());
            s.pop();
        }
        return v;
    }
};

#重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

##题解

前序遍历:根-左二子-右儿子,中序遍历:左二子-根-右儿子

可以根据前序遍历确定根,然后根据中序遍历确定左子树和右子树。并且可以递归的查根和左右子树

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
        TreeNode* f(vector<int> pre, int& start, vector<int> vin, int l, int r){
        if (start >= pre.size() || vin.empty() || r < l) {
            --start;            // 当子树为空时,前序游标start已经+1了,要减回去
            return NULL;
        }
        int i = l;
        while (i <= r){
            if (pre[start] == vin[i]) break;
            i++;
        }
        if (i > r) return NULL;
        TreeNode* Node = new TreeNode(pre[start]);
        Node->left = f(pre, ++start, vin, l, i-1);
        Node->right = f(pre, ++start, vin, i+1, r);
        return Node;
    }
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int start = 0;
        int l = 0, r = vin.size() - 1;
        return f(pre, start, vin, l, r);
    }
};

#用两个栈实现队列

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

##题解

栈的先进后出和队列的先进先出。push的话入栈就行了(可能会影响到pop,先这样看看),pop操作要找到最先入栈的元素,而根据栈的先进后出原则(假设栈a有元素,栈b为空时),把a中元素都弹到栈b,b的栈顶就是最先入栈的元素,pop掉就行了。而入栈时只往a中push,这样可以保证b中元素一定是在a中元素先进的队列。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if (!stack2.empty()) {
            int node = stack2.top();
            stack2.pop();
            return node;
        }
        while (!stack1.empty()){
            stack2.push(stack1.top());
            stack1.pop();
        }
        int node = stack2.top();
        stack2.pop();
        return node;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

#二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

输入描述:

二叉树的镜像定义:源二叉树 
    	    8
    	   /  \
    	  6   10
    	 / \  / \
    	5  7 9 11
    	镜像二叉树
    	    8
    	   /  \
    	  10   6
    	 / \  / \
    	11 9 7  5

##题解 二叉树前序遍历时把访问左右子树的顺序反过来即可,即根-右-左

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    TreeNode * Mirr(TreeNode *p) {
        if (p == NULL) return NULL;
        TreeNode *tmp = p->left;
        p->left = Mirr(p->right);
        p->right = Mirr(tmp);
        return p;
    }
    void Mirror(TreeNode *pRoot) {
        pRoot = Mirr(pRoot);
    }
};
/*No recurse*/
/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    TreeNode * Mirr(TreeNode *p) {
        if (p == NULL) return NULL;
        stack<TreeNode*> s;
        s.push(p);
        while (!s.empty()) {
            TreeNode * node = s.top();
            s.pop();
            if (!node) continue;
            TreeNode * tmp = node->left;
            node->left = node->right;
            node->right = tmp;
            s.push(node->left);
            s.push(node->right);
        }
        return p;
    }
    void Mirror(TreeNode *pRoot) {
        pRoot = Mirr(pRoot);
    }
};

#两个链表的公共节点

输入两个链表,找出它们的第一个公共结点。

##题解

法1:用一个集合,把其中一个链表的节点加入集合,再扫描另一个链表,当发现节点处于集合中即找到

法2:扫描得到链表长度差,让长链表先走长度差的步数然后再一起走并比较,若出现相等节点则找到

/*
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 || !pHead2) return NULL;
        set<ListNode*> Set;
        ListNode *p = pHead1;
        while (p) {
            Set.insert(p);
            p = p->next;
        }
        p = pHead2;
        while (p) {
            if (Set.find(p) != Set.end()) return *Set.find(p);
            p = p->next;
        }
        return NULL;
    }
};
/*法2*/
/*
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 || !pHead2) return NULL;
        int len1 = 0;
        ListNode *p1 = pHead1;
        while (p1) {
            len1++;
            p1 = p1->next;
        }
        ListNode* p2 = pHead2;
        int len2 = 0;
        while (p2) {
            len2++;
            p2 = p2->next;
        }
        int dif_len = abs(len1-len2);
        p1 = pHead1;
        p2 = pHead2;
        if (len1 > len2) {
            while (dif_len--) p1 = p1->next;
        }
        else {
            while (dif_len--) p2 = p2->next;
        }
        while (p1 || p2) {
            if (p1 == p2) return p1;
            p1 = p1->next;
            p2 = p2->next;
        }
        return NULL;
    }
};

#合并两个链表

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

##题解

简单的把小的放前面即可,归并排序的一步。不过要注意边界情况

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if (!pHead1) return pHead2;
        if (!pHead2) return pHead1;
        ListNode* p1 = pHead1;
        ListNode* p2 = pHead2;
        ListNode* res = new ListNode(0);//人比较懒,借O(1)空间建个虚的头结点
        ListNode* p = res;
        while (p1 || p2) {
            if (!p1 && p2) {
                p->next = p2;
                break;
            }
            if (!p2 && p1) {
                p->next = p1;
                break;
            }
            if (p1->val > p2->val) {
                p->next = p2;
                p2 = p2->next;
                p = p->next;
            }
            else {
                p->next = p1;
                p = p1;
                p1 = p1->next;
            }
        }
        return res->next;
    }
};

#数值的整次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

##题解

是个简单的题,但是考虑情况要全,基数为0,指数为0,大于0,小于0等。

class Solution {
public:
    double Power(double base, int exponent) {
        if (abs(base) < (1E-8)) return double (0.0);
        if (exponent == 0 ) return 1.0L;
        double res = 1.0L;
        if (exponent > 0) {
             while (exponent--) {
                 res *= base;
             }
            return res;
        }
        exponent = abs(exponent);
        while (exponent--) {
            res *= base;
        }
        return double(1.0)/res;
    }
};

#调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

##题解

扫描数组获取奇数个数n,把奇数放n前,偶数放n后。交换奇偶数位置注意只能相邻不断交换

class Solution {
public:
    void Swap (int &a, int &b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
    void reOrderArray(vector<int> &array) {
         int i=0, j = array.size();
        int odd = 0;
        for (auto c : array) 
            if (c & 1) odd++;
        while (i < odd) {
            while (i < odd && array[i] & 1) i++;
            if (i >= odd) break;
            int tmpi = i;
            i++;
            while (i < array.size() && !(array[i] & 1)) i++;
            for (j = i-1; j>= tmpi; j--) {
                Swap(array[j], array[i]);
                i = j;
            } 
            i = tmpi + 1;
        }
    }
};

#链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

##题解

扫描得到链表长度l,用l-k得到正数第几个节点。注意非法输入

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (!pListHead || k <= 0) return NULL;
        int len = 0;
        ListNode* p = pListHead;
        while (p) {
            p = p->next;
            len++;
        }
        if (len < k) return NULL;
        int diff = len - k;
        p = pListHead;
        while (diff--)
            p = p->next;
        return p;
    }
};

#栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

##题解

用一个标记序列来标记压入序列中已访问(弹出)的数字。首先找到最先出栈数字在压入序列的位置,标记为当前位置p及更新标记序列,继续查找出栈数字k:1.往p的右边查找至序列尾部,如找到则更新p的位置及标记序列并继续找下一个数字,否则2.往p的左边找,这里只需找在p左边且未被访问的最靠近p的数字,若该数字与出栈数字k想等则更新p的位置及标记序列并继续找下一个数字,否则返回FALSE。查找完毕则TRUE。

法2:用一个栈来模拟压入弹出。栈为空或者栈顶不是要出栈的数字时就压栈,栈顶为要出栈的数字时弹栈,否则返回false

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.empty() || popV.empty() || pushV.size() != popV.size()) return false;
        int p = 0,  i = 0;
        vector<int> visit(pushV.size(),0);
        while (pushV[p] != popV[i] && p < pushV.size()) p++;
        if (p == pushV.size()) return false;
        i++;
        visit[p] = 1;
        while (i < popV.size()) {
            int k;
            bool flag = false;
            for(k=p+1; k< pushV.size(); k++){
                if (pushV[k] == popV[i]) {
                    p = k;
                    visit[k] = 1;
                    i++;
                    flag = true;
                    break;
                }
            }
            if (flag) continue;
            k = p-1;
            while (visit[k]) k--;
            if (pushV[k] == popV[i]) {
                i++;
                p = k;
                visit[k] = 1;
                continue;
            }
            return false;
        }
        return true;
    }
};

/*法2*/
class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.empty() || popV.empty() || pushV.size() != popV.size()) return false;
        int p = 0,q = 0, len = pushV.size();
        stack<int> s;
        while (q < len) {
            while (s.empty() || (p<len && s.top() != popV[q])) {
                s.push(pushV[p]);
                p++;
            }
            if (s.top() != popV[q]) return false;
            s.pop();
            q++;
        }
        return true;
    }
};

#顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.

##题解

设四个变量行最小值rmin,行最大值rmax,列最小值cmin,列最大值cmax。先打印一行,rmin++;再打印一列,cmax--;再打印一行,rmax--;再打印一列,cmin++。循环打印,注意退出条件为rmin>rmax||cmin>cmax

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> v;
        if (matrix.empty()) return v;
        int rmin = 0, cmin = 0, rmax = matrix.size()-1, cmax = matrix[0].size()-1;
        int i = 0, j = 0;
        while (rmin <= rmax && cmin <= cmax) {
            for (j=cmin; j <= cmax; j++) v.push_back(matrix[rmin][j]);
            rmin++;
            if (rmin > rmax) break;
            for (i = rmin; i<= rmax; i++) v.push_back(matrix[i][cmax]);
            cmax--;
            if (cmin > cmax) break;
            for (j = cmax; j>= cmin; j--) v.push_back(matrix[rmax][j]);
            rmax--;
            if (rmin > rmax) break;
            for (i = rmax; i>= rmin; i--) v.push_back(matrix[i][cmin]);
            cmin++;
        }
        return v;
    }
};

# 从上到下打印二叉树

 

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

##题解

 

用一个队列把要访问节点的左右儿子加入队尾,每次访问都从队头取元素,访问完就pop掉。注意非法输入

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        deque<TreeNode*> q;
        q.push_back(root);
        vector<int> v;
        if (!root) return v;
        while (!q.empty()) {
            TreeNode * p = q.front();
            if (p->left) q.push_back(p->left);
            if (p->right) q.push_back(p->right);
            v.push_back(p->val);
            q.pop_front();
        }
        return v;
    }
};

#二叉树和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

##题解

dfs+回溯确定路径,求和加入数组中。最后根据数组长度对list排序

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int cmp(const vector<int> a, const vector<int> b) {
        return (vector<int>)a.size() > (vector<int>)b.size();
    }
    void dfs (TreeNode* root, vector<int> &path, vector<vector<int> > &v,const int exp) {
        if (!root) return;
        path.push_back(root->val);
        if (!root->left && !root->right) {
            int sum = 0;
            for (auto i : path)
                sum += i;
            if (sum == exp) v.push_back(path);
        }
        if(root->left) dfs(root->left, path, v, exp);
        if(root->right) dfs(root->right, path, v, exp);
        path.pop_back();
    }
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<vector<int> > v;
        vector<int> path;
        if (!root) return v;
        dfs (root, path, v, expectNumber);
        sort(v.begin(),v.end());
        return v;
    }
};

#二叉搜索数和双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

##题解

中序遍历就是二叉搜索树的排序。用队列把中序遍历存起来,然后从队头到队尾把相邻节点连起来

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void inorder (TreeNode* p, deque<TreeNode*> &d) {
        if (!p) return;
        if (p->left) inorder(p->left, d);
        d.push_back(p);
        if(p->right) inorder(p->right, d);
    }
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if (!pRootOfTree) return NULL;
        TreeNode * head ;
        TreeNode *p = pRootOfTree;
        while(p->left) p = p->left;
        head = p;
        //inorder(pRootOfTree, NULL);
        deque<TreeNode*> d;
        inorder(pRootOfTree,d);
        deque<TreeNode*>::iterator i,j;
        for(i = d.begin(); i!= d.end(); i++) {
            j = i;
            j++;
            if (j!=d.end()) {
                    (*i)->right = *j;
                    (*j)->left = *i;
            }
        }
        return head;
    }
};

#字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

##题解

法一:递归+回溯,第一个位置是所有字符都有可能出现的位置,填好第一个字符后,余下的字符可看成一个新串。之后进行类似操作,注意字符可能重复,要去重。

法二:递归+回溯,利用第一个位置是所有字符可能出现的位置,那么第一个字符和后面的所有字符依次交换即可,交换完后,后面的n-1个字符可看成新串进行类似操作。

//法一
class Solution {
public:
    void Per(string &str, set<string> &v, string &s) {
        if (str.empty()) {
            v.insert(s);
            return ;
        }
        for (int i=0; i<str.size(); i++) {
            string c = str.substr(i,1);
            str.erase(i,1);
            s.append(c);
            Per(str, v, s);
            str.insert(i,c);
            s.pop_back();
        }
    }
    vector<string> Permutation(string str) {
        set<string> v;
        vector<string> ret;
        if(str.empty()) return ret;
        string s;
        Per(str, v, s);
        for (auto c : v) ret.push_back(c);
        return ret;
    }
};
//法二
class Solution {
public:
    void Per(char *str, set<string> &v, char *s) {
        if (*s==0) {
            v.insert(string(str));
            return ;
        }
        for (char* i=s; *i!=0; i++) {
            char c = *s;
            *s = *i;
            *i = c;    
            Per(str, v, s+1);
            c = *s;
            *s = *i;
            *i = c;   
        }
    }
    vector<string> Permutation(string str) {
        set<string> v;
        vector<string> ret;
        char *s = (char*)str.data();
        if(str.empty()) return ret;
        Per(s, v, s);
        for (auto c : v) ret.push_back(c);
        return ret;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值