- 输入一个整数,输出该数二进制表示中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;
}
}
- 题目描述
给定一个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;
}
- 题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
参考代码
最简单的方法就是把奇数和偶数按顺序挑出来,分别放到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个节点。
关注要点
- 链表头指针是否为空,若为空则直接返回回null
- k是否为0,k为0也就是要查找倒数第0个节点,由于计数一般是从1开始的,所有输入0没有实际意义,返回null
- 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;
}
}
- 题目描述
输入一个链表,反转链表后,输出新链表的表头。
解题思路
设置三个指针,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;
}
}
- 题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路
两种解法:递归和非递归
/*
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;
}
}
- 题目描述
输入两棵二叉树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;
}
}
- 题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:
源二叉树
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);
}
}