剑指offer——2

  1. 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示
    解题思路
    如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
    举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n!=0){
            count += 1;
            n &= (n-1);
        }
        return count;
    }
}
  1. 题目描述
    给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
    解题思路
    指数为负时,可以先对指数求绝对值,算出次方的结果后再取倒数
    当底数为0,指数为负时,会出现对0求倒数情况,要特殊处理
    0的0次方在数学上没有意义,因此无论输出0还是1都是可以接受的
    在计算次方的时候,除了简单的遍历,我们可以使用递归的思想,如下公式,来减少计算量:在这里插入图片描述
public class Solution {
    public double Power(double base, int exponent) {
        int n = exponent;
        if(exponent==0){
            // 当指数为0底数为0时,没有意义,返回0或者返回1都可以
            return 1;
        }else if(exponent < 0){
            if(base == 0){
                throw new RuntimeException("分母不能为0"); 
            }
            n = -exponent;
        }
        double res = PowerUnsignedExponent(base, n);
        return exponent<0? 1/res: res;
  }
    public double PowerUnsignedExponent(double base, int n){
        if(n == 0)
            return 1;
        if(n == 1)
            return base;
        //递归
        double res = PowerUnsignedExponent(base, n/2);
        res *= res;
        if(n%2 == 1)
            res *= base;
        return res;
    }
}

代码优化
可以使用右移运算符代替除以2,用位与运算符代替求余运算符(%)来判断一个数是奇数还是偶数。

public double PowerUnsignedExponent(double base, int n){
        if(n == 0)
            return 1;
        if(n == 1)
            return base;
        //递归
        double res = PowerUnsignedExponent(base, n>>1);
        res *= res;
        if((n & 0x1) == 1)
            res *= base;
        return res;
}
  1. 题目描述
    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

参考代码
最简单的方法就是把奇数和偶数按顺序挑出来,分别放到vector里,最后再把偶数的vector接到奇数vector的末尾。

public class Solution {
    public void reOrderArray(int [] array) {
        Vector<Integer> odd = new Vector<Integer>();
        Vector<Integer> even = new Vector<Integer>();
        for(int i = 0; i < array.length; i++){
            if(array[i]%2 == 0){
                even.add(array[i]);
            }else{
                odd.add(array[i]);
            }
        }
        odd.addAll(even);
        for(int i=0;i<array.length;i++){
            array[i] = odd.get(i);
        }
    }
}

如果不能开僻额外的空间,可以尝试有类似于冒泡排序的方法,如果当前的值为偶数,后一个值为奇数,则两个数对换位置:
11. 题目描述
输入一个链表,输出该链表中倒数第k个结点
解题思路
经典的双指针法。定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动,从第k步开始,第二个指针也开始从链表的头指针开始遍历,由于两个指针的距离保持在k-1,当第一个指针到达链表的尾节点时,第二个指针刚好指向倒数第k个节点。
关注要点

  1. 链表头指针是否为空,若为空则直接返回回null
  2. k是否为0,k为0也就是要查找倒数第0个节点,由于计数一般是从1开始的,所有输入0没有实际意义,返回null
  3. k是否超出链表的长度,如果链表的节点个数少于k,则在指针后移的过程中会出现next指向空指针的错误,所以程序中要加一个判断
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null || k == 0)
            return null;
        ListNode temp = head;
        //判断k是否超过链表节点的个数,注意是 i < k - 1
        for(int i=0; i < k-1; i++){
            if(temp.next != null)
                temp = temp.next;
            else
                return null;
        }
        ListNode pA = head;
        ListNode pB = head;
        for(int i=0; i<k-1; i++)
            pA = pA.next;
        while(pA.next != null){
            pA = pA.next;
            pB = pB.next;
        }
        return pB;
    }
}
  1. 题目描述
    输入一个链表,反转链表后,输出新链表的表头。

解题思路
设置三个指针,head为当前节点,pre为当前节点的前一个节点,next为当前节点的下一个节点,需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2的过程中,用pre让节点反转所指方向,next节点保存next1节点防止链表断开

需要注意的点:
1、如果输入的头结点是null,则返回null
2、链表断裂的考虑

package dai.List;
/**
 *  输入一个链表,反转链表后,输出新链表的表头。
 *
 */
public class ReverseList02 {
	public ListNode02 reverseList(ListNode02 head) {
		if(head == null) {
			return null;
		}
		//最终需要实现的是 将列表1 -> 2 -> 3 -> 4 转换为1 <- 2 <-3 <- 4
		//为了能够快速反转列表,需要定义节点tem来保存当前节点的next值,定义pre来保存节点的上一个值
		ListNode02 tem = null;
		ListNode02 pre = null;
		while(head != null) {
			//保存当前节点的下一个节点,防止出现断列
			tem = head.next;
			//保存好之后,则将当前节点指向pre
			//例如,当前是第一次循环,则1 -> 2 转变为  null <- 1;方向发生改变,代码实现如下
			head.next = pre;
			//记住当前每个节点的状态,我们将节点都向后移一步
			pre = head;
			head = tem;
		}
		return pre;
	}
}
class ListNode02 {
	int val;
	ListNode02 next;
	public ListNode02(int val){
		this.val = val;
	}
}

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

解题思路
两种解法:递归和非递归

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
//递归解法
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null)
            return list2;
        else if(list2 == null)
            return list1;
        ListNode mergehead = null;
        if(list1.val <= list2.val){
            mergehead = list1;
            mergehead.next = Merge(list1.next,list2);
        }else{
            mergehead = list2;
            mergehead.next = Merge(list1, list2.next);
        }
        return mergehead;
    }
}
//非递归解法
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null)
            return list2;
        else if(list2 == null)
            return list1;
        ListNode mergehead = null;
        if(list1.val <= list2.val){
            mergehead = list1;
            list1 = list1.next;
        }else{
            mergehead = list2;
            list2 = list2.next;
        }
        ListNode cur = mergehead;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                cur.next = list1;
                list1 = list1.next;
            }else{
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        if(list1 == null)
            cur.next = list2;
        else if(list2 == null)
            cur.next = list1;
        return mergehead;
    }
}
  1. 题目描述
    输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构);
    解题思路
    递归思想,如果根节点相同则递归调用IsSubtree(),如果根节点不相同,则判断root1的左子树和roo2是否相同,再判断右子树和root2是否相同;
    注意节点为空的条件,HasSubTree中,只要有树为空就返回false; IsSubtree中,要先判断root2,如果root2为空,则说明第二棵树遍历完了,即匹配成功。
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1 == null || root2 == null){
            return false;
        }
        return IsSubtree(root1, root2) || 
               HasSubtree(root1.left, root2) ||
               HasSubtree(root1.right, root2);
    }
    public boolean IsSubtree(TreeNode root1, TreeNode root2){
        //要先判断roo2, 不然{8,8,7,9,2,#,#,#,#,4,7},{8,9,2}这个测试用例通不过。
        if(root2 == null)
            return true;
        if(root1 == null)
            return false;
        if(root1.val == root2.val){
            return IsSubtree(root1.left, root2.left) && 
                IsSubtree(root1.right, root2.right);
        }else
            return false;
    }
}
  1. 题目描述
    操作给定的二叉树,将其变换为源二叉树的镜像。

输入描述:
二叉树的镜像定义:

 源二叉树 
        8
       /  \
      6   10
     / \  / \
    5  7 9 11
    镜像二叉树
        8
       /  \
      10   6
     / \  / \
    11 9 7  5

解题思路
通过对以上两棵树的观察,我们可以总结出这两棵树的根节点相同,但它们的左、右两个子节点交换了位置。所以我们可以得出求一棵树的镜像的过程:先前序遍历这棵树的每个节点,如果遍历到的节点有子节点,就交换它的两个子节点。当交换完所有非叶节点的左、右子节点之后,就得到了树的镜像。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        //当前节点为空,直接返回
        if(root == null)
            return;
        //当前节点没有叶子节点,直接返回
        if(root.left == null && root.right == null)
            return;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        //递归交换叶子节点
        if(root.left != null)
            Mirror(root.left);
        if(root.right != null)
            Mirror(root.right);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39411208/article/details/88299729
今日推荐