文章目录
11. 二进制中1的个数
题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
解题思路:n&(n-1)
这一操作会消除最低位的1。一直这样操作,并同时更新n的值,当n的值为0的时候,结束循环。
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n != 0){
count++;
n = n & (n-1);
}
return count;
}
}
12. 数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
解题思路:采用非递归快速幂的方式进行求解。在指数exponent不等于0的情况下执行语句,当 (exponent & 1)==1
的时候,说明该指数的二进制数所对应的数为1,令 result*=base
。在循环语句中,每次更新 base 的值,令 base*=base
,exponent右移一位。
public class Solution {
public double Power(double base, int exponent) {
if(exponent < 0){
base = 1/base;
exponent = -exponent;
}
double result = 1.0d;
while(exponent != 0){
if((exponent&1) == 1){//说明这一位有1,将x乘进结果
result *= base;
}
base *= base;
exponent >>= 1;
}
return result;
}
}
13. 调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解题思路:采用 in-place 方法,记录一个变量 slow 表示已经将奇数放好的下一个位置,显然最开始 slow=0
表示还没有一个奇数放好。fast 表示数组的下标,初始值为0,表示从下标0开始遍历。如果遇到偶数,fast++
;如果遇到奇数,假设位置为 fast,就将此奇数插入到 slow 所指的位置,然后i往后移动一个位置,在插入之前,显然会涉及到数据的移动,也就是将[slow, fast-1]
整体往后移动。直到整个数组遍历完毕。
public class Solution {
public void reOrderArray(int [] array) {
int slow = 0;//用于定位第一次出现偶数时所对应得数组下标
int fast = 0;//用于遍历整个数组
while(fast < array.length){
if( array[fast]%2 != 0 ){//奇数
int temp = array[fast];
for(int i = fast; i > slow; i--){
array[i] = array[i-1];
}
array[slow] = temp;
slow++;
fast++;
} else{
fast++;
}
}
}
}
14. 链表中倒数第k个结点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
解题思路:采用双指针的方法。设置两个指针(slow,fast),先让fast先行k步,然后slow和fast指针同步前进,直到fast走到链表尾部,slow指针所指向就是倒数第k个结点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head == null || k <= 0){
return null;
}
ListNode slow = head;
ListNode fast = head;
//快指针先走k步
for(int i = 0; i < k; i++){
if(fast == null){//如果中途快指针走到头了,说明k大于链表长度(不可忽略)
return null;
}
fast = fast.next;
}
while(fast != null){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
15. 反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
解题思路:头插法
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode pre = null;
ListNode next = null;
while(head != null){
next = head.next;//记录当前结点的下一个结点
head.next = pre;
pre = head;
head = next;//更新当前结点
}
return pre;
}
}
16. 合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路:
初始化:定义cur指向新链表的头结点
操作:
(1)如果 list1 指向的结点值小于等于 list2 指向的结点值,则将 list1 指向的结点值链接到 cur 的 next 指针,然后 list1 指向下一个结点值;
(2)否则,让 list2 指向下一个结点值
(3)循环步骤(1)和步骤(2),直到 list1 或者 list2 为 null
(4)将 list1 或者 list2 剩下的部分链接到 cur 的后面
技巧:一般创建单链表,都会设一个虚拟头结点,也叫哨兵,因为这样每一个结点都有一个前驱结点。
/*
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;
}
if(list2 == null){
return list1;
}
ListNode merge = new ListNode(-1);
ListNode cur = merge;
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 = list1;
}
if(list2 != null){
cur.next = list2;
}
return merge.next;
}
}
17. 树的子结构
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
解题思路:递归
递归函数的功能:判断2个树是否有相同的结构,如果相同,返回true,否则返回false
递归终止条件:
(1)如果树B为空,返回true,此时,不管树A是否为空,都为true
(2)否则,如果树B不为空,但是树A为空,返回false,此时B还没空但A空了,显然false
下一步递归参数:
(1)如果A的根节点和B的根节点不相等,直接返回false
(2)否则,相等,就继续判断A的左子树和B的左子树,A的右子树和B的右子树
/**
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(root2 == null || root1 == null){
return false;
}
if(root1.val == root2.val && recur(root1.left, root2.left) && recur(root1.right, root2.right)){
return true;
}
return HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
}
private boolean recur(TreeNode root1,TreeNode root2) {
if(root2 == null){
return true;
}
if(root1 == null){
return false;
}
if(root1.val == root2.val){
return recur(root1.left, root2.left) && recur(root1.right, root2.right);
}else{
return false;
}
}
}
18. 二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
解题思路:
镜像就是将“根”节点的左右两个“子”节点互换,类似于数组的元素交换(运用临时节点temp),利用二叉树的广度优先搜索即可
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public void Mirror(TreeNode root) {
// //递归版本
// if(root == null){
// return;
// }
// TreeNode temp = root.left;
// root.left = root.right;
// root.right = temp;
// Mirror(root.left);
// Mirror(root.right);
//非递归版本——利用二叉树的广度优先搜索 层次遍历
if(root == null){
return;
}
TreeNode temp, cur;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int len = queue.size();
for(int i = 0; i < len; i++){
cur = queue.poll();
//先交换左右子树 再入队
temp = cur.left;
cur.left = cur.right;
cur.right = temp;
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
}
}
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.
解题思路:
不断地收缩矩阵的边界,定义四个变量代表范围,up、down、left、right
(1)向右走存入整行的值,当存入后,该行再也不会被遍历,代表上边界的 up 加一,同时判断是否和代表下边界的 down 交错
(2)向下走存入整列的值,当存入后,该列再也不会被遍历,代表右边界的 right 减一,同时判断是否和代表左边界的 left 交错
(3)向左走存入整行的值,当存入后,该行再也不会被遍历,代表下边界的 down 减一,同时判断是否和代表上边界的 up 交错
(4)向上走存入整列的值,当存入后,该列再也不会被遍历,代表左边界的 left 加一,同时判断是否和代表右边界的 right 交错
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
return list;
}
int up = 0;
int down = matrix.length - 1;
int left = 0;
int right = matrix[0].length - 1;
while(true){
//最上面一行
for(int col = left; col <= right; col++){
list.add(matrix[up][col]);
}
//向下逼近
up++;
//判断是否越界
if(up > down){
break;
}
//最右边一行
for(int row = up; row <= down; row++){
list.add(matrix[row][right]);
}
//向左逼近
right--;
//判断是否越界
if(left > right){
break;
}
//最下面一行
for(int col = right; col >= left; col--){
list.add(matrix[down][col]);
}
//向上逼近
down--;
//判断是否越界
if(up > down){
break;
}
//最左边一行
for(int row = down; row >= up; row--){
list.add(matrix[row][left]);
}
//向右逼近
left++;
//判断是否越界
if(left > right){
break;
}
}
return list;
}
}
20. 包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
解题思路:
双栈法。一个栈用来存放所有数据,另外使用辅助栈,每次把最小值保存在栈中。
//方法1:辅助栈中存放的是不包含重复元素的最小值
public class Solution {
private static Stack<Integer> stack1 = new Stack<Integer>();//存放所有元素
private static Stack<Integer> stack2 = new Stack<Integer>(); //作为辅助栈,保存最小值
//入栈
public void push(int node) {
stack1.push(node);
if(stack2.isEmpty() || stack2.peek() > node){
stack2.push(node);//出现最小值才入栈,否则不操作
}
}
//出栈
public void pop() {
if(stack1 != null){
//不相等,说明当前弹出的不是最小值
if(stack1.peek() != stack2.peek()){
stack1.pop();
}else{
stack1.pop();
stack2.pop();
}
}
}
//获取栈顶元素
public int top() {
return stack1.peek();
}
//获取最小元素
public int min() {
return stack2.peek();
}
}
//方法2:辅助栈中存放的最小值是包含重复元素的
public class Solution {
private static Stack<Integer> stack1 = new Stack<Integer>();//存放数据
private static Stack<Integer> stack2 = new Stack<Integer>();//存放最小值
public void push(int node) {
stack1.push(node);
if(stack2.isEmpty() || stack2.peek() > node){
stack2.push(node);
}else{
stack2.push(stack2.peek());
}
}
public void pop() {
stack1.pop();
stack2.pop();
}
public int top() {
return stack1.peek();
}
public int min() {
return stack2.peek();
}
}