剑指offer21-40

21.栈的压入弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
1.创建一个栈,模拟人工判断的方式就可;

import java.util.ArrayList;
import java.util.Stack;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack stack = new Stack();
        int i=1,j=0;
        stack.push(pushA[0]);
        while (!stack.empty()){
            if(popA[j]==(int)stack.peek()){
                j++;
                stack.pop();
            }
            else if(i==pushA.length)
                return false;
            else{
                stack.push(pushA[i++]);
            }
        }
        return true;
    }
}

22.从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。
1.层次遍历,队列实现;注:所有都须注意边界检测。

23.二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
1.递归:刚看到没思路,后来想想后序遍历的特点;

public class Solution {
	public boolean VerifySquence(int [] sequence,int start,int end) {
		if (start>=end)
			return true;
		int k=0,flag=sequence[end];
		int i=start,j=end-1;
		while (i<j){
			if (sequence[i]<flag){
				i++;
			}
			else if (flag<sequence[j]){
				j--;
			}
            else break;
		}
		if (i==j)
			return VerifySquence(sequence,start,i-1)&&VerifySquence(sequence,j,end-1);
		return false;
	}
	public boolean VerifySquenceOfBST(int [] sequence) {
        if (sequence.length==0)
			return false;
		return VerifySquence(sequence,0,sequence.length-1);
	}

}

2.非递归,遇到比最后一个元素大的节点,就说明它的前面都比最后一个元素小,该元素后面的所有值都必须大于最后一个值:

bool VerifySquenceOfBST(vector<int> sequence) {
    if(sequence.size()==0){
        return false;
    }
    int sum = sequence.size()-1;
    int count = 0;
    while(sum){
        while(sequence[count] < sequence[sum]){
            ++count;
        }
        while(sequence[count] > sequence[sum]){
            ++count;
        }
        if(count < sum){
            return false;
        }
        --sum;
        count = 0;
    }
    return true;
}

3.最大最小边界约束法:从根节点出发往下走,高层祖辈节点序列就会不停地对低层未遍历节点形成一个上下限约束。
注:边界要注意and递归的三大部分:true、false、递归体。

24.二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
1.递归。
注:list是递归最外层,不是每层递归都是新的list,不过可以每层调用时new一个;可将和转变为减,后面只需判断是否与0相等;注意list.remove这行解决的问题。

public class Solution {
    private ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
	private ArrayList<Integer> list = new ArrayList<>();
	public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
		if(root == null)return result;
		list.add(root.val);
		target -= root.val;
		if(target == 0 && root.left == null && root.right == null)
			result.add(new ArrayList<Integer>(list));
		FindPath(root.left, target);
		FindPath(root.right, target);
		list.remove(list.size()-1);
		return result;
	}
}

25.复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
1.map:用一个 hashmap 建立新旧链表节点的对应的结点关系;

public class Solution {
	public RandomListNode Clone(RandomListNode pHead)
	{
		Map<RandomListNode, RandomListNode> map =new HashMap();
		RandomListNode p=pHead;
		while (p!=null){
			map.put(p,new RandomListNode(p.label));
			p=p.next;
		}
		p=pHead;
		while (p!=null){
			map.get(p).next=map.get(p.next);
			map.get(p).random=map.get(p.random);
			p=p.next;
		}
		return map.get(pHead);
	}
}

2.三次遍历:遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;拆分链表,将链表拆分为原链表和复制后的链表。

26.二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
1.线索二叉树。

public class Solution {
    TreeNode pre=null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree==null)
            return null;
        Convert(pRootOfTree.right);
        if (pre!= null){
            pRootOfTree.right=pre;
            pre.left=pRootOfTree;
        }
        pre=pRootOfTree;
        Convert(pRootOfTree.left);
        return pre;
    }
}

注:自己想的这种思路:

public TreeNode convert(TreeNode pRootOfTree, TreeNode behindNode) {
		if (pRootOfTree==null)
			return null;
		TreeNode preNode= convert(pRootOfTree.left, pRootOfTree);
		pRootOfTree.left = preNode;
		pRootOfTree.right = behindNode;
		p=pRootOfTree;
		convert(pRootOfTree.right, pRootOfTree);
		return pRootOfTree;
	}

不能实现,因为左子树和右子树对前后节点定义不同。

27.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba(输入数字n,打印从1到最大的n位数)。
1.递归,这个题卡了半天,须注意的点有点多:

import java.util.ArrayList;
import java.util.Collections;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> result = PermutationHelp(new StringBuilder(str));
        Collections.sort(result);
        return result;
    }
    public ArrayList<String> PermutationHelp(StringBuilder str) {
        ArrayList<String> result = new ArrayList<String>();
        if (str.length() == 1) result.add(str.toString());
        for (int i = 0; i < str.length(); i++) {
            if (i== 0||str.charAt(i) != str.charAt(0)) {
                char x = str.charAt(i);
                str.setCharAt(i, str.charAt(0));
                str.setCharAt(0, x);
                ArrayList<String> newResult = PermutationHelp(new StringBuilder(str.substring(1)));
                for (int j = 0; j < newResult.size(); j++)
                    result.add(str.substring(0, 1) + newResult.get(j));
                x = str.charAt(i);
                str.setCharAt(i, str.charAt(0));
                str.setCharAt(0, x);
            }
        }
        return result;
    }
}

在这里插入图片描述
在这里插入图片描述

28.数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
1.用Map;
2.用preValue记录上一次访问的值,count表明当前值出现的次数,如果下一个值和当前值相同那么count++;如果不同count–,减到0的时候就要更换新的preValue值了,因为如果存在超过数组长度一半的值,那么最后preValue一定会是该值。

29.最小的K个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
1.堆排,快排;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
 
public class TopK
{
    
    /**
     * 取最小的k个数 
     */
    public static int[] getTopKByPartition(int[] arr, int k) {
        if (arr == null || arr.length <= 0 || k <= 1) {
            return null;
        } 
        int size = arr.length;
        int target = k;
        int low = 0;
        int high = size - 1;
        int mid = getMid(arr, low, high);
        while (mid != target) {
            if (mid < target) {
                mid = getMid(arr, mid + 1, high);
            } else {
                mid = getMid(arr, low, mid - 1);
            }
        }
        int[] ret = new int[target];
        System.arraycopy(arr, 0, ret, 0, target);
        return ret;
    }
    /**
     * 快排思想-一趟排序
     */
    private static int getMid(int[] arr, int low, int high) {
        int base = arr[low];
        while (low < high) {
            // 判断条件必须加=场景,为<= 不能为<,否则数组中有相同数据时,会一直循环
            while (low < high && base <= arr[high]) {
                high--;
            }
            arr[low] = arr[high];
            
            // 判断条件必须加=场景,为>= 不能为>,否则数组中有相同数据时,会一直循环
            while (low < high && base >= arr[low]) {
                low++;
            }
            arr[high] = arr[low];
        }
        arr[low] = base;
        return low;
    }
       
    /**
     * 堆排序,组建一个(size+1)/2大小的最大堆 ,取到top (size+1)/2个小的值,则堆顶元素即为中位数
     */
    public static Queue<Integer> getTopKByMaxHeap(int[] arr, int k) {
        if (arr == null || arr.length <= 0 || k <= 1) {
            return null;
        }
        // 初步组建最大堆
        int heapSize = k;
        // JDK1.7 需指定初始大小,内部实现了最大堆(实现自定义从大到小排序)
        PriorityQueue<Integer> queue = new PriorityQueue<>(heapSize, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2)
            {
                return o2 - o1;
            }
        });
        for (int i=0; i<heapSize; i++) {
            queue.offer(arr[i]);
        }
        // 比较余下元素,比堆顶小则替换堆顶元素为新值
        int size = arr.length;
        for (int j=heapSize;j<size;j++) {
            int temp = arr[j];
            // 当前元素比堆顶大 则删除堆顶元素,把当前元素加入堆
            if (queue.peek() > temp) {
                queue.poll();
                queue.offer(temp);
            }
        }
        // 返回堆顶元素
        return queue;
    }
    
    public static void main(String[] args)
    {
        int[] arr = {4,5,6,1,2,0,3,7,8,9,10,1,1,1,1,1,1};
        System.out.println(Arrays.toString(getTopKByPartition(arr, 10)));        
        int[] arr2 = {4,5,6,1,2,0,3,7,8,9,10,1,1,1,1,1,1};
        System.out.println(getTopKByMaxHeap(arr2, 10));
    }
}

2.冒泡、选择,插入;

30.连续子数组的最大和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
1.用max记录即可。

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            array[i] += array[i - 1] > 0 ? array[i - 1] : 0;
            max = Math.max(max, array[i]);
        }
        return max;
    }
}

31.整数中1出现的次数(从1到n整数中1出现的次数)

求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
1、感觉是数学问题,李志雄都不会,建议放弃。

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int cnt = 0;
        for (int m = 1; m <= n; m *= 10) {
            int a = n / m, b = n % m;
            cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
        }
        return cnt;
    }
}

32.把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
1.比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面;

33.丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
1.维持三个指针来记录当前乘以2、乘以3、乘以5的最小值。

34.第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
1.hashmap or长度为52的数组遍历两遍。(注意hashmap的使用)

35.数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
1.归并排序,参考左神。

36.两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
1.双指针法;
2.a+b==b+a;
3.左神(又有简单的map法!!!)。

37.数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。
1.二分查找(上限下限分开查找)。

38.二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
1.分治法;

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if (!pRoot) return 0;
        int lval = TreeDepth(pRoot->left);
        int rval = TreeDepth(pRoot->right);
        return max(lval, rval) + 1;

    }
};

2.层次遍历。

39.平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
1.自顶向下;

class Solution {
public:
    map<TreeNode*, int> hs;
    int depth(TreeNode *root) {
        if (!root) return 0;
        if (hs.find(root) != hs.end()) return hs[root];
        int ldep = depth(root->left);
        int rdep = depth(root->right);
        return hs[root] = max(ldep, rdep) + 1;
    }
    bool judge(TreeNode *root) {
        if (!root) return true;
        return abs(hs[root->left] - hs[root->right]) <= 1 && 
        judge(root->left) && judge(root->right);
    }
    bool IsBalanced_Solution(TreeNode* root) {
        depth(root);
        return judge(root);
    }
};

2.自底向上。

class Solution {
public:
    int depth(TreeNode *root) {
        if (!root) return 0;
        int ldep = depth(root->left);
        if (ldep == -1) return -1;
        int rdep = depth(root->right);
        if (rdep == -1) return -1;
        int sub = abs(ldep - rdep);
        if (sub > 1) return -1;
        return max(ldep, rdep) + 1;
    }
    bool IsBalanced_Solution(TreeNode* root) {
        return depth(root) != -1;
    }
};

40.数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
1、哈希法;
2、位运算,假设AB是出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值