7.用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路:一个栈压入元素,而另一个栈作为缓冲.
- 当插入时,直接插入 stack1;
- 当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素。
解题技巧:出栈时,先把stack2中元素按顺序出栈,然后再把stack1中所有元素倒序进入stack2.
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() throws Exception{
if (stack1.isEmpty() && stack2.isEmpty()) {
throw new Exception("栈为空!");
}
if (stack2.size() <= 0) {
while (stack1.size() != 0) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
10.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路:a&(a-1)的结果会将a最右边的1变为0,直到a = 0。如果a为正数,还可以先将a&1 != 0,然后右移1位
public int NumberOf1(int n) {
int count = 0;
while (n != 0) {
count++;
n = (n-1) & n;
}
return count;
}
12.打印从1到最大的n位数
思路:当输入的n很大的时候,我们求最大的n位数是不是用整型(int)或者长整型(long long)都会溢出?也就是说我们需要考虑大数问题。
- 使用字符串或数组表示数,
- 数组第0位表示n位数的最高位,递归调用,给数组每个元素赋值,直到循环到数组最后位;
- 开始打印数组,打印数组时;
- 打印数组时,从开头第一个不为0的元素开始打印。
第一次 0 0 0 … 0 0
public void printToMaxOfNDigits(int n) {
int[] array=new int[n];
if(n <= 0)
return;
printArray(array, 0);
}
private void printArray(int[] array,int n) {
for(int i = 0; i < 10; i++) {
if(n != array.length) {
array[n] = i;
//递归赋值,直到最后一位开始循环打印
printArray(array, n+1);
} else {
boolean isFirstNo0 = false;
for(int j = 0; j < array.length; j++) {
if(array[j] != 0) {
System.out.print(array[j]);
if(!isFirstNo0)
isFirstNo0 = true;
} else {
if(isFirstNo0)
System.out.print(array[j]);
}
}
System.out.println();
return ;
}
}
}
13.O(1)时间删除链表节点
思路:不需要找到将要删除节点的前一个节点;将要删除节点的下一个节点的值赋给要删除的节点,然后指向下下一个节点。
14.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路:每次只和前面一个数交换位置。或者利用辅助数组。
只需要找到并向前调整奇数,偶数相对就实现调整。因为前面数组是已经调整好的,所以把奇数前面的所有偶数向后移动一位,直到遇到一个奇数停止。
public void reOrderArray(int [] array) {
if(array == null)
return ;
for(int i = 1; i < array.length; i++){
int temp = array[i];
int j = i - 1;
if(array[i] % 2 != 0){//奇数
while(j >= 0){
if(array[j] % 2 != 0){
//遇到奇数,说明前面已经全部是奇数,不需要往前判断
break;
}else{//遇到偶数,需要把所有偶数向后移动
int t = array[j+1];
array[j+1] = array[j];
array[j] = t;
j--;
}
}
}
array[j+1] = temp;
}
}
15.输入一个链表,输出该链表中倒数第k个结点。
扩展题:找中间节点,使用两个指针,一个走一步,一个走两步。找到中间节点
思路:定义一快一慢两个指针,快指针走K步,然后慢指针开始走,快指针到尾时,慢指针就找到了倒数第K个
节点。
16.输入一个链表,反转链表后,输出链表的所有元素。
思路:定义两个指针,反向输出。把旧链表节点按顺序一个一个插入新链表的头部,一个指针(temp)指向新链表的头部,一个指针(P)指向旧链表的头结点的下一位,把旧链表动态头结点(head)插入到新链表的头部
扩展题:输出反转后链表的头节点,定义三个指针反向输出。
public ListNode ReverseList(ListNode head) {
if (head == null) {
return null;
}
ListNode temp = null;
while(head != null) {
ListNode p = head.next;
head.next = temp;
temp = head;
head = p;
}
return temp;
}
16.输入一个链表,反转链表后,输出链表的所有元素。
思路:将链表节点,采用头部插入法插入,生成的新链表即为反转链表。
public ListNode reverseList(ListNode head){
if(head == null){
return null;
}
ListNode newHead = null;
ListNode temp = null;
while (head != null){
temp = head.next;
head.next = newHead;
newHead = head;
head = temp;
}
return newHead;
}
找两个链表公共节点
什么是公共节点,并不是两个节点的值相同就是公共节点。
而是在第一链表和第二链表中都存在一个节点,该节点往后的子链表在两个链表中是相同的。
方法一:
如果我们从两个链表的尾部开始往前比较,那么最后一个相同的节点就是我们要找的节点。分别将两个链表的节点放入两个栈中,这样栈顶就是两个链表的尾节点,比较两个栈顶节点是否相同,如果相同,将栈顶弹出比较下一个栈顶,直到找到最后一个相同的栈顶。时间复杂度O(m + n)。
方法二:
先获得两个链表的长度,然后在较长的链表上先走若干步(两链表长度之差),接着同时在两个链表上遍历,找到的第一个相同的节点就是他们的第一个公共节点。时间复杂度O(m + n)。
方法三:
用两个指针同时分别扫描”两个链表“,当指针到达null时从另一个链表开始扫描,最终两个指针到达公共结点走的步数是一样。
(m+n-x),其中m,n分别为两个链表长度,x为链表的公共长度。
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2){
if(pHead1 == null || pHead2 == null){
return null;
}
ListNode temp1 = pHead1;
ListNode temp2 = pHead2;
while (temp1 != temp2){
temp1 = (temp1 == null? pHead2 : temp1.next);
temp2 = (temp2 == null? pHead1 : temp2.next);
}
return temp1;
}
30.求一个数组的top k个最大元素。
思路,用长度为K的数组表示小顶堆,取数组前K个元素构成一个小顶堆,剩下数组元素与小顶堆的根比较,如果比根大,则替换根元素,并进行堆排序。
堆排序思路:
- K个数组构建小顶堆:从下层第一个非叶子开始,与左右子节点比较,把左右子节点中的最小那个元素替换父节点。
- 堆顶元素替换后排序:比较根节点与左右子节点,选择最小那个元素和父节点替换位置,同时对替换位置后的左(右)子节点进行排序。
public int[] sort(int[] input, int k){
if(input == null || input.length == 0){
return null;
}
int length = input.length;
if(length <= k){
return input;
}
int[] result = new int[k];
//构建长度为k的堆
for(int i = 0; i< k; i++){
result[i] = input[i];
}
//从最后一个非叶子节点开始进行排序
for(int i = k/2 -1; i >= 0; i--){
minHeap(result, i);
}
//数组后面元素与小顶堆比较,如果大于则替换堆顶元素
for (int i = k; i< length; i++){
if(input[i] > result[0]){
result[0] = input[i];
minHeap(result, 0);
}
}
return result;
}
//i表示待排序的元素的索引下标
public void minHeap(int[] array, int i){
int left = 2*i + 1;
int right = left + 1;
int min = i;
int length = array.length;
if(left < length && array[left] < array[min]){
min = left;
}
if(right < length && array[right] < array[min]){
min = right;
}
if(min != i){
int temp = array[i];
array[i] = array[min];
array[min] = temp;
minHeap(array, min);
}
}
31.求连续子数组(包含负数)的最大和
思路:如果连续子数组和小于0,则将最大和置为当前元素值。
public int findGreatestSumOfSubArray(int[] array){
if(array == null || array.length == 0){
return 0;
}
int currentSum = array[0];
int greatest = currentSum;
int length = array.length;
for (int i = 1; i < length; i++){
if(currentSum < 0){
currentSum = array[i];
}else {
currentSum += array[i];
}
if(greatest < currentSum){
greatest = currentSum;
}
}
return greatest;
}
给定一棵二叉搜索树,请找出其中的第k小的结点。
思路:二叉搜索树左节点> 父节点 > 右节点。 因此找最新K节点先遍历左子树,再遍历根节点,然后遍历右子树,采用中序遍历方式即可。用栈保存父节点,然后遍历左子树。
链接:https://www.nowcoder.com/questionTerminal/ef068f602dde4d28aab2b210e859150a?answerType=1&f=discussion
来源:牛客网
import java.util.Stack;
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot == null || k <= 0){
return null;
}
Stack<TreeNode> stack = new Stack<>(); //建立栈
TreeNode cur = pRoot;
//while 部分为中序遍历
while(!stack.isEmpty() || cur != null){
if(cur != null){
stack.push(cur); //当前节点不为null,应该寻找左儿子
cur = cur.left;
}else{
cur = stack.pop();//当前节点null则弹出栈内元素,相当于按顺序输出最小值。
if(--k == 0){ //计数器功能
return cur;
}
cur = cur.right;
}
}
return null;
}
}
40.一个整型数组里除了两个数字之外,其他的数字都出现了两次。找出这两个只出现一次的数字。
思路:两个相同的数字异或运算之后为0;将所有元素做异或运算,结果为那两个只出现一次的数字做异或运算;找出计算结果1出现的位置,在这个位置这两个数一个为0,一个为1;把数组分为两部分,在这个位置为0的一组,为1的一组,分别做异或运算,两组结果即为这两个数字(0^a = a)。
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if (array == null)
return;
num1[0] = 0;
num2[0] = 0;
int number = array[0];
for (int i = 1; i < array.length; i++)
number ^= array[i];
// 异或后的数1出现在第几位
int index = 0;
while ((number & 1) == 0) {
number = number >> 1;
index++;
}
for (int i = 0; i < array.length; i++) {
// 判断第index位是不是0
boolean isBit = ((array[i] >> index) & 1) == 0;
if (isBit) {
num1[0] ^= array[i];
} else {
num2[0] ^= array[i];
}
}
}