剑指offer之每日6题 ----- 第三天

原题连接

1.调整数组顺序使奇数位于偶数前

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

思路1

两个思路,第一个是仿冒泡排序,遍历数组,前一个是偶数,后一个数是奇数,就交换,也就是前偶后奇就交换
时间复杂度:O(n^2)
空间复杂度:O(1)

public void reOrderArray(int [] array) {
        //前偶后奇就交换
        if (array.length ==0)
            return;
        for (int i = 0; i < array.length; i++) {
            for (int j = array.length-1; j > i; j--) {
                if ((array[j] & 1) !=0 && (array[j-1] & 1) ==0){
                    int temp = array[j];
                    array[j] =array[j-1];
                    array[j-1] = temp;
                }
            }
        }
    }
思路2

可以采用辅助数组的形式,建立一个和原数组等长的辅助数组,遍历原数组,遇见奇数就放从0开始放,遇见偶数就从奇数的末尾开始放
时间复杂度:O(n)
空间复杂度:O(n)

 public void reOrderArray(int [] array) {
        int[] newArray = new int[array.length];
        int oddCount = 0;
        int oddBegin = 0;

        for (int i = 0; i < array.length; i++) {
            if ((array[i] & 1) != 0)
                oddCount++;
        }

        for (int i = 0; i < array.length; i++) {
            if ((array[i] & 1) != 0) {
                newArray[oddBegin++] = array[i];
            } else {
                newArray[oddCount++] = array[i];
            }
        }

        for (int i = 0; i < array.length; i++) {
            array[i] = newArray[i];
        }
    }
扩展

如果把后面的条件就是“奇数和奇数,偶数和偶数的相对位置不变”去掉,这就是原微软的面试题,这里也说下思路和代码
思路就是遍历这个数组,一个从头开始,向后移动,直到找到第一个偶数,一个从尾开始,向前移动找到第一个奇数开始,交换这两个数,重复过程,直到两个指针相等为止

 public void reOrderArray3(int[] array) {
        int beginIndex = 0;
        int endIndex = array.length - 1;
        while (beginIndex < endIndex) {
            while (beginIndex < endIndex && (array[beginIndex] & 1) != 0)
                beginIndex++;
            while (beginIndex < endIndex && (array[endIndex] & 1) == 0)
                endIndex--;

            if (beginIndex < endIndex) {
                int temp = array[beginIndex];
                array[beginIndex] = array[endIndex];
                array[endIndex] = temp;
            }
        }
    }

2.链表中的倒数第K个结点

题目描述

输入一个链表,输出该链表中倒数第k个结点

思路

前后指针法,就是第一个指针先走k步,然后第二个指针再从链表的头指针开始走,这样当第一个指针走到链表的尾节点时候,第二个指针刚好走到倒数第k个节点。
要注意链表的节点总数不能少于k个

 public class ListNode {
        int val;
        ListNode next = null;

        ListNode(int val) {
            this.val = val;
        }
    }
 public ListNode FindKthToTail(ListNode head, int k) {
        if (head == null || k == 0)
            return null;
        ListNode begin = head;
        ListNode behind;
        for (int i = 0; i < k; i++) {
            if (head.next != null) {
                begin = begin.next;
            } else {
                return null;
            }
        }
        behind = head;
        while (begin.next != null) {
            behind = behind.next;
            begin = begin.next;
        }
        return behind;
    }
扩展

求链表的中间节点。如果链表中的节点总数为奇数,则返回中间节点的;如果是偶数,则返回中间两个节点的其中一个就行
同样也可以使用两个指针,同时从链表的头节点出发,第一个指针一次走一步,第二个指针一次走两步,这样当第二个指针到达链表的尾节点的时候,第一个指针正好走在链表的中间节点处。

3.反转链表

题目描述

输入一个链表,反转链表后,输出新链表的表头。

思路

有两种做法,第一种就是递归,就是把头节点后面的链表都反转过来,然后把头节点接到最后

  public class ListNode {
        int val;
        ListNode next = null;

        ListNode(int val) {
            this.val = val;
        }
    }
 public ListNode ReverseList(ListNode head) {
        if (head == null || head.next == null)
            return head;

        ListNode pre =ReverseList(head.next);
        head.next.next=head;
        head.next=null;
        return pre;
    }

第二种思路就是非递归,如果要反转一个链表,就需要知道这个链表当前遍历到的节点,它的前一个和后一个节点。

  public ListNode ReverseList2(ListNode head) {
        ListNode reverseHead = null;
        ListNode node = head;
        ListNode prev = null;
        
        while (node != null) {
            ListNode next = node.next;
            if (next == null)
                reverseHead = node;
            node.next = prev;
            prev = node;
            node = next;
        }
        return reverseHead;
    }

4.合并两个排序的链表

题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

思路

也是一样有两种解法,一种递归一种非递归,就是去比较这两个链表头节点,哪个值小返回哪个值,然后合并链表,以此比较

 //递归
 public ListNode Merge(ListNode list1,ListNode list2) {
          if (list1 == null)
            return list2;
        if (list2==null)
            return list1;
        if (list1.val < list2.val){
            list1.next = Merge(list1.next,list2);
            return list1;
        } else{
            list2.next = Merge(list1,list2.next);
            return list2;
        }
    }

5.树的子结构

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

思路

先判断B是不是A的子结构,再判断B是不是A左子树的子结构,然后再判断是B是不是A右字数的子结构

  public boolean HasSubtree(TreeNode root1, TreeNode root2) {
        boolean result = false;

        if (root1 != null && root2 != null) {
            if (root1.val == root2.val)
                result = isSubTree(root1,root2);
            if (!result)
                result = HasSubtree(root1.left,root2);
            if (!result)
                result = HasSubtree(root1.right,root2);
        }
        return result;
    }

    private boolean isSubTree(TreeNode root1, TreeNode root2){
        if (root2 == null)
            return true;
        if (root1 ==null && root2 !=null)
            return false;
        if (root1.val == root2.val){
            return isSubTree(root1.left,root2.left) && isSubTree(root1.right,root2.right);
        }else{
            return false;
        }

    }

6.二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

思路

将根节点的左右子树节点都转换成镜像,然后把叶子节点的值交换

 public void Mirror(TreeNode root) {
        if (root == null)
            return;

        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right= temp;
    }

猜你喜欢

转载自blog.csdn.net/YuQing_Cat/article/details/86139861