《剑指offer》Java版(一)

3.二维数组中的查找

5.从尾到头打印链表

6.重建二叉树

8.旋转数组的最小数字

9.斐波那契数列

10.二进制数中1的个数

15.链表中倒数第k个节点

16.反转链表

17.合并2个排序的链表

19.二叉树的镜像

23.从上往下打印二叉树

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

26.复杂链表的复制

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

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

31.连续子数组的最大和

34.丑数

37.两个链表的第一个公共节点

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

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

41.和为s的两个数字VS和为s的连续正数序列

43.n个骰子的点数

44.扑克牌的顺子

47.不用加减乘除做加法

3.二维数组中的查找

/* 从排序二维数组查找数据
     * 数组从左往右递增,从上往下递增
     * 以右上角元素作为基准匹配,若小于基准,则不可能在最右列,于是right--,若大于基准,则不可能在第一行,up++
     */
    public boolean searchFromDimetricArray(int num,int[][] array)
    {
        boolean result =false;
        if(array==null||array.length==0) return result;
        int up=0;
        int down = array.length-1;
        int left = 0;
        int right = array[0].length-1;
        while(up<=down&&left<=right)
        {
            while(up<=down&&(array[up][right]<num)) up++;
            if(up>down) break;
            if(array[up][right]==num)
            {
                result = true;
                System.out.println("坐标:"+(up+1)+","+(right+1));
                break;
            }
            else right--;
            while(left<=right&&(array[down][left]<num)) left++;
            if(left>right) break;
            if(array[down][left]==num)
            {
                result = true;
                System.out.println("坐标:"+(down+1)+","+(left+1));
                break;
            }
            else down--;
        }
        return result;
    }

5.从尾到头打印链表

//逆序输出链表,借助栈实现
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode)
    {
        ArrayList list = new ArrayList();
        if(listNode==null) {return list;}
        Stack<Integer> stack = new Stack<Integer>();
        while(listNode!=null)
        {
            stack.push(listNode.val);
            listNode = listNode.next;
        }
        while(!stack.isEmpty())
        {
            list.add(stack.pop());
        }
        return list;
    }
    //逆序输出链表,递归实现
    public ArrayList<Integer> printListFromTailToHead1(ListNode listNode)
    {
        ArrayList list = new ArrayList();
        if(listNode!=null)
        {
            if(listNode.next!=null) list=printListFromTailToHead1(listNode.next);
            //System.out.println(listNode.val);
            list.add(listNode.val);
        }
        return list;
        
    }

6.重建二叉树

//根据前序和后序遍历序列重构二叉树
    public static TreeNode rebuildTree(int[] preorder,int[] inorder)
    {
        if(preorder==null||inorder==null||preorder.length!=inorder.length
                ||preorder.length==0) return null;
        TreeNode root = new TreeNode(preorder[0]);
        //查找中序中根节点的索引,从而分开左子树和右子树
        int i=0;
        for(;i<inorder.length;i++)
        {
            if(inorder[i]==preorder[0]) break;
        }
        //分别设置左右子节点
        //若前序的第一个元素不等于中序的第一个元素,则存在左子节点
        //中序的根节点后有元素,则存在右子节点
        if(preorder[0]!=inorder[0]) root.left = new TreeNode(preorder[1]);
        if(i<inorder.length-1) root.right = new TreeNode(preorder[i+1]);
        int start = 0;
        int end = i;
        rebuildTreeCore(root.left, preorder, inorder, 1,start, i-1);
        rebuildTreeCore(root.right, preorder, inorder, i+1, i+1, preorder.length-1);
        return root;
    }
    
    public static void rebuildTreeCore(TreeNode root,int[] preorder,int[] inorder,int start_pre,int start,int end)
    {
        if(root==null||preorder==null||inorder==null||preorder.length!=inorder.length
                ||preorder.length==0||start>=end||(end-start)>preorder.length) return;
        
        //查找根节点在中序中的位置
        int i=start;
        for(;i<=end;i++) 
            if(inorder[i]==preorder[start_pre]) break;
        //计算当前树的左子树长度
        int length_left = i-start;
        //如果左子树长度大于0,则左子节点必然存在,且为当前树根节点的下一个节点
        if(length_left>0) root.left = new TreeNode(preorder[start_pre+1]);
        //如果没有右子树,则中序中根节点必然是最后一个节点,否则右子节点必然在前序子序列中根节点往右移动的左子树长度的位置
        if(i<end) root.right = new TreeNode(preorder[start_pre+length_left+1]);
        start_pre++;//左右子树都判断完了,前序序列搜索起始索引向前走1位(不管匹配到左子树没,前序的当前起始位已经匹配过了,都需要向后走1位)
        rebuildTreeCore(root.left,preorder,inorder,start_pre,start,i-1);
        //右子树的前序的起始点为向右偏移左子树的长度
        rebuildTreeCore(root.right, preorder, inorder, start_pre+length_left,i+1, end);
         
    }

8.旋转数组的最小数字

/*
     * 旋转数组的最小数字
     * 此旋转数组可能是一个每个元素都相等的数组,也可能经过周期的旋转后复原了
     * 以低位比较相对复杂一点,因为数组是递增的
     */
    public static int searchFromRotatedArray(int[] arr)
    {
        if(arr==null||arr.length==0) return -1;
        int low = 0,high = arr.length-1;
        int mid =(low+high)/2;
        while(low<high)
        {
            
            if(arr[mid]>arr[low])
                {
                    if(arr[mid]>arr[high]) low =mid+1;
                    else high=mid;
                }
            else if(arr[mid]<arr[low]) high = mid;
            else{
                if(arr[mid]>arr[high]) low++;
                else high = mid;
            }
            mid = (low+high)/2;
        }
        return mid;
    }
    
    //旋转数组的最小数字,与高位比较
    public static int searchFromRotatedArray1(int[] array)
    {
        if(array==null||array.length==0) return -1;
        int low = 0,high = array.length-1;
        int mid = (low+high)/2;
        while(low<high)
        {
            //若中位数大于高位数,则一定旋转了,且不是所有值相等
            if(array[mid]>array[high]) low = mid+1;
            //若中位数小于高位数,则最小值一定在前半段
            else if(array[mid]<array[high]) high=mid;
            //若相等,则可能旋了,也可能没旋
            else{
                if(array[mid]>=array[low]) high = mid;//若大于低位则表示旋了,且在前半段,等于表示没旋,也在前半段
                else low = mid;//若小于则在后半段,且旋了
            }
            mid = (low+high)/2;
        }
        return mid;
    }

9.斐波那契数列

 /*从底向上计算斐波那契数列比递归求解效率高的多

*/

public long fibonacciFromBottom(int n)
    {
        long[] fi = {0,1};
        if(n<2) return fi[n];
        long fibonacciMinusTwo =0l;
        long fibonacciMinusOne =1l;
        long fibonacci =1l;
        for(int i=1;i<n;i++)
        {
            fibonacci = fibonacciMinusOne + fibonacciMinusTwo;
            fibonacciMinusTwo = fibonacciMinusOne;
            fibonacciMinusOne = fibonacci;
        }
        return fibonacci;
    }
 

10.二进制数中1的个数

// 计算一个整型数据的二进制中1的个数,
    /*
     * 思路:将自身和自身减1按位与,效率最高 
     * 拓展1:判断一个数是否是2的次方。拓展2: *
     */
    public static int countOneOfNumber(int n) {
        int temp = n;
        int count = 0;
        while (temp != 0) {
            count++;
            temp = temp & (temp - 1);
        }
        return count;
    }

 

/*
     * 不使用与、或以及位移等运算,计算整型数据的二进制中的1的个数
     * 将int *2相当于逻辑左移一位,若移位后<0,则表示高位为1,低位会补0
     */
    public int countOneOfNumber1(int iValue)
    {
        int count =0;
        while(iValue!=0)
        {
            if(iValue<0) count++;
            iValue *=2;
        }
        return count;
    }

15.链表中倒数第k个节点

//查找链表倒数第k个节点
    public ListNode kNodeOfBottom(ListNode pHead, int k)
    {
        if(pHead==null||k<=0) return null;
        ListNode pAhead = pHead;
        ListNode pBehead = pHead;
        while(k>0)
        {
            pAhead = pAhead.next;
            k--;
            //为null表示走到了链尾下一个,若k不为0,说明链表不够k长,继续执行循环会报空指针异常
            if(pAhead ==null) break;
        }
        if(k>0) return null;//大于0表示链表的长度不够k
        while(pAhead!=null)
        {
            pAhead = pAhead.next;
            pBehead = pBehead.next;
        }
        return pBehead;
    }

 

16.反转链表

/*
     * 反转链表
     * 需要小心头结点的next设为null,不然会成为循环链表
     * 在修改下一个即将转向的节点前,先保存其下一个节点的引用
     */
    public ListNode reverseLink(ListNode pHead)
    {
        if(pHead==null||pHead.next==null) return pHead;
        ListNode nextNode = pHead.next;
        pHead.next = null;
        while(nextNode!=null)
        {
            ListNode temp =nextNode.next;
            nextNode.next =pHead;
            pHead = nextNode;
            nextNode = temp;
        }
        return pHead;
    }

17.合并2个排序的链表

/*
     * 合并2个递增排序的链表
     * 本实现为不去重的,去重的需要加一个判断,也较为简单
     * 本实现会破坏原来的2个链表,合并后只剩1个
     */
    public ListNode combineSortedLinks(ListNode head1,ListNode head2)
    {
        if(head1==null) return head2;
        if(head2==null) return head1;
        ListNode currentNode = (head1.val<=head2.val)?head1:head2;
        ListNode head = currentNode;
        if(currentNode==head1) head1 = head1.next;
        else head2 = head2.next;
        
        while(head1!=null&&head2!=null)
        {
            if(head1.val<=head2.val)
            {
                //如果需要去重,这里需要判断currentNode与head1的值,并将下2行括起
                currentNode.next =head1;
                currentNode = currentNode.next;
                head1 = head1.next;
            }
            else
            {
                //如果需要去重,这里需要判断currentNode与head2的值,并将下2行括起
                currentNode.next = head2;
                currentNode = currentNode.next;
                head2 = head2.next;
            }
        }
        if(head1==null) currentNode.next = head2;
        if(head2==null) currentNode.next = head1;
        return head;
    }

19.二叉树的镜像

/*
     * 二叉树的镜像
     * 遍历,交互左右子节点
     */
    public void mirrorOfTree(TreeNode root)
    {
        if(root==null) return;
        TreeNode node = root;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        //stack.push(node);
        while(node!=null||!stack.isEmpty())
        {
            while(node!=null)
            {
                TreeNode temp = node.left;
                node.left = node.right;
                node.right = temp;
                stack.push(node);
                node = node.left;
            }
            node = stack.pop().right;
            
        }
    }

23.从上往下打印二叉树(数的层序遍历)

/*
     * 从上往下打印二叉树,即树的层序遍历
     * 思路:借助队列,首先将根节点入队列,然后循环,每从队列取出一个元素则将其左右子节点入队列,直到队列空
     */
    public void visitByLevel(TreeNode pHead)
    {
        if(pHead==null) return;
        TreeNode node = pHead;
        Deque<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(node);
        while(/*node!=null||*/!queue.isEmpty())
        {
            node = queue.remove();
            System.out.println("当前节点的值:"+node.getValue());
            
            if(node.left!=null) queue.add(node.left);
            if(node.right!=null) queue.add(node.right);
            //node =null;
        }
    }

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

/*
     * 二叉树中和为某一值的路径
     * 思路:借助2个栈,1个用于中序遍历,1个用于记录当前路径,这2个栈的元素是不一致的,所以不能缩减为1个
     */
    public ArrayList<ArrayList<Integer>> findPath(TreeNode root,int target) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        if(root==null) return list;
        //树的访问栈
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode node = root;
        //记录当前路径
        Stack<TreeNode> p_stack = new Stack<TreeNode>();
        while(node!=null||!stack.isEmpty())
        {
            while(node!=null)
            {
                p_stack.push(node);
                //只需要计算叶子节点的路径
                if(node.left==null&&node.right==null)
                {
                    int sum = 0;
                    for(TreeNode x:p_stack) sum+= x.value;
                    if(sum==target){
                        ArrayList<Integer> temp = new ArrayList<Integer>();
                        for(TreeNode x:p_stack) temp.add(x.value);
                        list.add(temp);
                        /*p_stack.clear();
                        for(TreeNode y:stack) p_stack.push(y);
                        p_stack.push(node);*/
                    }
                }
                 
                stack.push(node);
                node = node.left;
                 
            }
            
            //栈顶元素的左子树已经访问完了,需要出栈,访问右子树,node为待访问的元素
            
            node =stack.pop();

            //如果路径栈的栈顶元素不是当前待访问元素,则路径栈出栈
            while(!p_stack.isEmpty()&&node!=p_stack.peek()) p_stack.pop();
            node =node.right;
        }
        return list;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值