《剑指offer第二版》 一

一、数组中的逆序对

(1)题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5

(2)题目分析

首先想的是将数组排序,在排序的过程中,比较前后数字的大小,然后统计逆序的个数。而在排序算法中,归并排序可以很好的解决这个问题。在归并排序的过程中,将两个有序的子数组合并时,如果数组A中的某一个数字大于数组B中的某个数字,那么数组A中该数字后面的所有数字均大于数组B中的这个数字。将所有的这种情况加起来,便得到了这个数组中的逆序对的总数。

(3)代码

package swordOffer;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-03-20 13:34
 * @desc 数组中的逆序对
 */
public class t1 {
    int sum = 0;
    public int reversePairs(int[] nums) {
        return mergeSort(nums, 0, nums.length - 1);
    }

    /**
     * 归并排序
     *
     * @param arr
     * @param begin
     * @param end
     */
    public int mergeSort(int[] arr, int begin, int end) {
        if (begin >= end) {
            return 0;
        }
        int mid = (begin + end) / 2;
        mergeSort(arr, begin, mid);
        mergeSort(arr, mid + 1, end);
        return merge(arr, begin, mid, end);
    }
    /**
     * 合并2个有序数组
     *
     * @param arr
     * @param begin
     * @param mid
     * @param end
     */
    public int merge(int[] arr, int begin, int mid, int end) {
        int temp[] = new int[end - begin + 1];
        for (int index = 0, i = begin, j = mid + 1; index < temp.length; index++) {
            if (i > mid) {
                temp[index] = arr[j++];
            } else if (j > end) {
                temp[index] = arr[i++];
            } else if (arr[i] <= arr[j]) {
                temp[index] = arr[i++];
            } else {
                temp[index] = arr[j++];
                sum += mid - i + 1;
            }
        }
        for (int j = 0; j < temp.length; j++) {
            arr[begin + j] = temp[j];
        }
        return sum;
    }
    public static void main(String[] args) {
        int arr[] = {1, 6, 5, 8, 2, 3, 9, 4, 7};
        t1 mm = new t1();
        System.out.println(mm.reversePairs(arr));
    }
}

二、两个链表的第一个公共节点

(1)题目描述

输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
图片: https://uploader.shimo.im/f/jUeM58wIMHUFwYAE.png
在节点 c1 开始相交。

(2)题目分析

对于两个链表,如果长度一样,那么通过遍历这两个链表,依次比较每一个节点是否相同即可,而如果长度不一样,则难以进行比较。那么只要想办法让这两个链表长度相同,便较容易求出公共节点,而A+B的长度一定等于B+A长度,因此,当遍历的时候,如果某一个链表结束了,则将它的最后节点指向另外一个链表的头结点即可。

(3)代码

package swordOffer;

import charpter2.ListNode;


/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-03-20 20:37
 * @desc 两个链表的第一个公共节点
 */
public class t2 {
    public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode tempA = headA;
        ListNode tempB = headB;
        while (tempA != tempB) {
            tempA = tempA == null ? headB : tempA.next;
            tempB = tempB == null ? headA : tempB.next;
        }
        return tempA;
    }

    public static void main(String[] args) {
        ListNode listNode11 = new ListNode(4);
        ListNode listNode12 = new ListNode(1);
        ListNode listNode13 = new ListNode(8);
        ListNode listNode14 = new ListNode(4);
        ListNode listNode15 = new ListNode(5);
        listNode11.next = listNode12;
        listNode12.next = listNode13;
        listNode13.next = listNode14;
        listNode14.next = listNode15;

        ListNode listNode21 = new ListNode(5);
        ListNode listNode22 = new ListNode(0);
        ListNode listNode23 = new ListNode(1);

        listNode21.next = listNode22;
        listNode22.next = listNode23;
        listNode23.next = listNode13;

        ListNode res = getIntersectionNode(listNode11, listNode21);
        if (res != null) {
            System.out.println(res.val);
        }
    }
}

三、在排序数组中查找数字

(1)题目描述

统计一个数字在排序数组中出现的次数。

示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

(2)题目分析

只要是遇到排序数组中的查找,最优的解法一般都是二分法,而这题不是简单的查找,而是统计一个数字出现的次数,因此需要将二分法进行一定的改造。当中间数字小于目标数字时,则递归左数组,当中间数字大于目标数字时,则递归右数组,而当中间数字等于目标数字时,这时候需要考虑左右数组中均包含目标数字的情况,因此需要递归左右数组。这样便可统计出目标数字出现的次数。

(3)代码

package swordOffer;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-03-20 21:33
 * @desc 在排序数字中查找数字I
 */
public class t3 {
    int num = 0;

    public static void main(String[] args) {
        int nums[] = {5, 7, 7, 8, 8, 10};
        t3 tt = new t3();
        System.out.println(tt.search(nums, 8));
    }

    public int search(int[] nums, int target) {
        binarySearch(nums, 0, nums.length - 1, target);
        return num;
    }

    public void binarySearch(int[] nums, int begin, int end, int tatget) {

        if (begin > end) {
            return;
        }
        int mid = (begin + end) / 2;
        if (nums[mid] > tatget) {
            binarySearch(nums, begin, mid - 1, tatget);
        } else if (nums[mid] < tatget) {
            binarySearch(nums, mid + 1, end, tatget);
        } else {
            num++;
            binarySearch(nums, begin, mid - 1, tatget);
            binarySearch(nums, mid + 1, end, tatget);
        }
    }
}

四、数组中重复的数字

(1)题目描述

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

(2)题目分析

最简单的方法肯定是暴力遍历法,使用一个额外的集合,当数字不在集合中时,加入集合,当集合中存在该数字时,则直接返回即可。然后这种因为使用了额外的空间,因此不是最优解。而通过分析题意可以知道,数组的长度为n,数组中的数字范围在0~n-1,很明显,可以利用桶排序的思想,当遍历到一个数字时,如果该数字不在它原本应该在的位置,则把它与它应该在的位置上的数字进行交换,当然,如果它应该在的位置上如果已经存在了这个数字,那么便是重复的数字,直接返回即可。

(3)代码

package swordOffer;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-03-21 09:48
 * @desc 数组中重复的数字
 */
public class t4 {
    public static int findRepeatNumber1(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int temp;
            while (nums[i] != i) {
                if (nums[i] == nums[nums[i]]) {
                    return nums[i];
                }
                temp = nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5, 5, 6, 7};
        System.out.println(findRepeatNumber1(nums));
    }
}

五、二维数组中的查找

(1)题目描述

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。

(2)题目分析

根据上述二维数组的特性,选择第一行的最后一个数字作为起始点,当目标数字大于该数字时,则往下移动一格,当目标数字小于该数字时,则向左移动一格,直到找到该目标数字就返回true,如果到达边界还找不到则返回false。

(3)代码

package swordOffer;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-03-21 10:50
 * @desc 二维数组中的查找
 */
public class t5 {
    public static boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0) {
            return false;
        }
        int colLength = matrix.length;
        int rowLength = matrix[0].length;
        int i = 0;
        int j = rowLength - 1;
        while (i < colLength && j >= 0) {
            if (target < matrix[i][j]) {
                j--;
            } else if (target > matrix[i][j]) {
                i++;
            } else {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int[][] matrix = {
                {1, 4, 7, 11, 15},
                {2, 5, 8, 12, 19},
                {3, 6, 9, 16, 22},
                {10, 13, 14, 17, 24},
                {18, 21, 23, 26, 30}
        };
        System.out.println(findNumberIn2DArray(matrix, 166));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值