剑指offer|11-20题解题思路及代码(Java版)

剑指offer11到20题总览:

  1. 二进制中1的个数
  2. 数值的整数次方
  3. 调整数组顺序使奇数位于偶数前面
  4. 链表中倒数第k个结点
  5. 反转链表
  6. 合并两个排序的链表
  7. 树的子结构
  8. 二叉树的镜像
  9. 顺时针打印矩阵
  10. 包含min函数的栈

11、二进制中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

解题思路

如果一个整数n(包括0、正数和负数)不等于0,则该整数一定有至少一位为1。如果将n减一,则最右边的1会变为0,原最右边的1的右边的所有0都会变成1。例如n=1100,n-1=1011。我们将n和n-1做与运算,的n&(n-1)=1000,得到的1000相当于去掉了1100中的最右边的1。如此重复,直到n变为0,就可以得到n中包含的1的个数。

注意事项

  1. 整数包括了负数。
  2. 循环条件是n!=0。
public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n != 0){//循环直到去掉n中所有的1为止
            n = n & (n-1);//去掉n中最右边的1
            count++;
        }
        return count;
    }
}

12、数值的整数次方

题目描述

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

解题思路

此题求幂,如果按照将base乘abs(exponent)次计算,则时间复杂度为O(n),参考快速幂的方法,可以将求幂运算的时间复杂度降为O(\log n)。例如求a^{11},将11化成二进制为1011。则\begin{aligned} a^{11}&=a^{2^0}*a^{2^1}*a^{2^3}\\ &=a^1*a^2*a^8 \end{aligned}

注意事项

  1. 输入数据判断:底数base等于0时返回0,exponent为0时返回1,exponent为1时返回base。
  2. 当exponent为负时,先按照exponent为正计算,然后返回幂的倒数。
public class Solution {
    public double Power(double base, int exponent) {
        if(base == 0){return 0;}
        if(exponent == 1){return base;}
        double res = 1;//res为返回结果
        double curr = base;//curr表示如果当前位为1,则res需要乘上的数
        int n = Math.abs(exponent);//对指数求绝对值,先按正数计算
        while(n!=0){//我们将将指数n的二进制数不断右移一位
            if((n & 1) == 1){//如果n最右边的一位为1
                res = res * curr;//则res乘上当前位为1需要乘上的curr
            }
            curr = curr * curr;//curr变为原来数的平方是每次n右移一位都要做的
            n = n >> 1;
        }
        return exponent>0? res : 1/res;
    }
}

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

题目描述

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

解题思路

一种是排序的方法,只要排序的算法是稳定的就行。再就是新建数组的方法。

import java.util.ArrayList;

public class Solution {
    public void reOrderArray(int [] array) {
        ArrayList<Integer> odd = new ArrayList<>();//奇数
        ArrayList<Integer> even = new ArrayList<>();//偶数
        for(int i=0; i<array.length; i++){
            if(array[i]%2 == 1){
                odd.add(array[i]);
            }else{
                even.add(array[i]);
            }
        }
        for(int i=0; i<odd.size(); i++){
            array[i] = odd.get(i);
        }
        for(int i=0; i<even.size(); i++){
            array[i+odd.size()] = even.get(i);
        }
    }
}

14、链表中倒数第k个结点

题目描述

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

解题思路

用两个指针,理想情况下,指针p先走k-1步,然后p和q一起走,直到指针p走到链表尾,提出q.val即可。

注意事项

  1. 检查输入:当输入链表为空时返回null;当k=0时返回null。
  2. 当链表长度小于k时,返回null。
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null){return null;}//链表为空时返回null
        if(k==0){return null;}//k等于0时返回null
        ListNode p = head;
        ListNode q = head;
        int count = 0;//count记录p提前走的步数
        while(count < k-1){
            if(p.next!=null){//如果p可以往后走,则往后走
                p = p.next;
                count++;
            }else{//如果p无法往后走,而此时count还没有达到k-1步,则链表长小于k,返回null
                return null;
            }
        }
        while(p.next != null){//p和q同时往后走
            p = p.next;
            q = q.next;
        }
        return q;
    }
}

15、反转链表

题目描述

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

解题思路

设置两个指针p和q,p一直指向原head,q指向p的next。我们将p的next指向的节点插入到新head的前面,并成为新head。如下图:
剑指offer15|反转链表

注意事项

  1. 输入数据判断:链表为空则返回null;只有一个节点则不需要反转,直接返回head。
  2. 插入节点的时候,指针变化的前后顺序。
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null){return null;}
        if(head.next == null){return head;}
        ListNode p = head;
        ListNode q;
        while(p.next != null){//将q插到最前面,成为新head
            q = p.next;
            p.next = q.next;
            q.next = head;
            head = q;
        }
        return head;
    }
}

16、合并两个排序的链表

题目描述

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

解题思路

可以用非递归也可以用递归的方法。比较list1和list2的头结点的val,如果list1小则新链表接上list1的头结点,将list1剩下的部分和list2再递归进行Merge。

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){return list2;}
        if(list2 == null){return list1;}
        ListNode head = null;
        if(list1.val <= list2.val){
            head = list1;
            head.next = Merge(list1.next, list2);//将list1剩下的部分和list2合并
        }else{
            head = list2;
            head.next = Merge(list1, list2.next);//将list1和list2剩下的部分合并
        }
        return head;
    }
}

17、树的子结构

题目描述

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

解题思路

本题是判断子结构而不是子树,也就是,当我们发现root1的某个结点与root2的根节点的val相同后,我们判断其是否是子结构时,如果root2中的结点在root1中都对应相同,但是root1后面还有一些多的结点,这种也属于子结构。也就是,root2中的叶子结点,在root1中不一定非要也是叶结点,还有一些子结点也是可以的。
基本的思路没有什么特殊的技巧,就是在root1中找到与root2根节点val相同的值,然后判断root2是否与root1中的一样。

public class Solution {
    public boolean isSub(TreeNode root1,TreeNode root2){
        //root1==null root2!=null ->false
        //root1==null root2==null ->true
        //root1!=null root2==null ->true,只需是子结构就可以
        //root1!=null root2!=null ->判断val,如果val不相等则返回false,val相等则递归左右子树
        if(root1 == null && root2 != null) return false;
        if(root2 == null) return true;
        if(root1.val != root2.val) return false;
        return isSub(root1.left, root2.left) && isSub(root1.right, root2.right);
    }
    public boolean HasSubtree(TreeNode root1,TreeNode root2)
    {
        boolean result = false;
        //root1==null root2==null ->false
        //root1!=null root2==null ->false
        //root1==null root2!=null ->false
        //root1!=null root2!=null ->判断val是否相等,如果相等则调用isSub(),如果不相等则递归左右子树,root2不变
        if(root1 != null && root2 != null){//只需要判断root!=null && root2!=null的情况
            if(root1.val == root2.val){
                result = isSub(root1,root2);//如果根结点val一样,则调用isSub
            }
            if(!result){
                result = (HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2));
                //如果根节点不一样,则对root1的左右子树进行递归
            }
        }
        return result;
    }
}

18、二叉树的镜像

题目描述

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

解题思路

就是对树中每个结点的左右子树进行交换。

public class Solution {
    public void Mirror(TreeNode root) {
        if(root != null){
            TreeNode temp = root.left;//交换左右子树
            root.left = root.right;
            root.right = temp;
            Mirror(root.right);//将左子树进行镜像
            Mirror(root.left);//将右子树进行镜像
        }
    }
}

19、顺时针打印矩阵

题目描述

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

解题思路

没有什么特别的,就是需要计算清楚边界,比较麻烦。

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> list = new ArrayList<>();
        if(matrix.length == 0){return list;}
        if(matrix[0].length == 0){return list;}
        int row_num = matrix.length;//原矩阵行数
        int column_num = matrix[0].length;//原矩阵列数
        int row_current = row_num;//内部圈行数,每次循环-2
        int column_current = column_num;//内部圈列数,每次循环-2
        while(row_current>1 && column_current>1){//如果内部圈行或列为1后面再考虑,这里考虑内圈行列大于1的情况
            int margin = (row_num - row_current)/2;//计算内圈距离外圈的边界厚度
            int i = margin;//循环从内圈的左上顶点开始
            int j = margin;
            for(; j<column_current+margin-1; j++){//向右
                list.add(matrix[i][j]);
            }
            for(; i<row_current+margin-1; i++){//向下
                list.add(matrix[i][j]);
            }
            for(; j>margin; j--){//向左
                list.add(matrix[i][j]);
            }
            for(; i>margin; i--){//向上
                list.add(matrix[i][j]);
            }
            row_current -= 2;//循环完之后行列-2
            column_current -=2;
        }
        if(row_current == 1){//当内圈行数为1时,一次循环就行
            int margin = (row_num-1)/2;
            int column_len = column_num - 2*margin;
            for(int i=0; i<column_len; i++){
                list.add(matrix[margin][margin+i]);
            }
            column_current = 0;//因为我将行列分开判断,这里需要将剩余列数置为0
        }
        if(column_current == 1){//当内圈列数为1时
            int margin = (column_num-1)/2;
            int row_len = row_num - 2*margin;
            for(int i=0; i<row_len; i++){
                list.add(matrix[margin+i][margin]);
            }
        }
        return list;
    }
}

20、包含min函数的栈

题目描述

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

解题思路

使用两个栈,一个数据栈data_stack,一个最小值栈min_stack。
当push数据时,如果数据栈为空,则将数据push到数据栈,同时将数据push到最小值栈;如果数据栈不为空,则将数据push到数据栈,将数据与最小值栈栈顶元素比较,如果比栈顶元素小,则将新数据push到最小值栈,如果比栈顶元素大,则将栈顶元素再push一遍到最小值栈。
当pop数据时,数据栈和最小值栈同时pop。

注意事项:

  1. 获取栈顶元素的方法是stack.peek();
public class Solution {
    Stack<Integer> data_stack = new Stack<>();
    Stack<Integer> min_stack = new Stack<>();
    public void push(int node) {
        if(data_stack.empty()){//如果栈为空,则将node分别push到数据栈和最小值栈
            data_stack.push(node);
            min_stack.push(node);
        }else{//如果栈不为空,则要与最小值栈的栈顶元素进行比较,将较小者push到最小值栈中
            data_stack.push(node);
            if(node > min_stack.peek()){
                min_stack.push(min_stack.peek());
            }else{
                min_stack.push(node);
            }
        }
    }
    
    public void pop() {//题目规定没有返回值
        data_stack.pop();
        min_stack.pop();
    }
    
    public int top() {//java中栈获取栈顶元素是stack.peek();
        return data_stack.peek();
    }
    
    public int min() {//直接获取最小值栈的栈顶元素即可
        return min_stack.peek();
    }
}

结尾

如果您发现我的文章有任何错误,或对我的文章有什么好的建议,请联系我!如果您喜欢我的文章,请点喜欢~*我是蓝白绛,感谢你的阅读!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容