算法题——Find the Duplicate Number(JAVA)

本文介绍了一种利用堆排序解决寻找数组中重复数字的问题的方法。通过构建最大堆并进行堆排序,可以实现在O(n log n)的时间复杂度内完成排序,并在O(1)的空间复杂度下找出数组中唯一的重复数字。

题目描述:
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.

读题:
1.时间复杂度O(n^2)
2.空间复杂度O(1)
3.只有一个数字是重复的

知识储备:
堆排序同时满足时间和空间复杂度要求。
堆是一棵完全二叉树。使用最大堆,最大堆的最大元素一定在第0位置,其操作内容如下:对于每个节点i,我们考察他与子节点的大小,如果他比某个子节点小,则将他与子节点中最大的那个互换位置,然后在相应的子节点位置重复操作,直到到达堆的叶子节点或者考察的位置比子节点的值都要大为止。
构建好堆之后,交换0位置元素与顶,将heapsize减1,然后再在根节点出调用最大堆过程将新的堆重新最大堆化。依次循环,我们每次都能将现有堆中最大的元素放到堆末尾。最后就完成了整个排序过程。堆排序为原位排序(空间小), 且最坏运行时间是O(nlgn),是渐进最优的比较排序算法。

解题思路:
1.使用堆排序排列数组
2.将每一个元素与下一个元素的值进行比较,若相等,则该值为重复的数字

提交代码:

public class Solution {
    int[] heap;
    int heapsize;
    public int findDuplicate(int[] nums) {
        int re = 1;
        this.heap = nums;    
        this.heapsize = heap.length;
        BuildMaxHeap();
        HeapSort();
        for(int i = 0; i < nums.length - 1; i++) {
            if (nums[i] == nums[i+1]) {
                re = nums[i];
                break;
            }
        }
        return re;
    }
    public void BuildMaxHeap() {
        for (int i = heapsize / 2 - 1; i >= 0; i--) {
            Maxify(i);
        }
    }
    public void Maxify(int i) {
        int l = Left(i);
        int r = Right(i);
        int largest;
        if (l < heapsize && heap[l] > heap[i])
            largest = l;
        else
            largest = i;
        if (r < heapsize && heap[r] > heap[largest])
            largest = r;
        if (largest == i || largest >= heapsize)
            return;
        int tmp = heap[i];
        heap[i] = heap[largest];
        heap[largest] = tmp;
        Maxify(largest);
    }
    public void HeapSort() {
        for (int i = 0; i < heap.length; i++) {
            int tmp = heap[0];
            heap[0] = heap[heapsize-1];
            heap[heapsize-1] = tmp;
            heapsize--;
            Maxify(0);
        }
    }
    private int Parent(int i) {
        return (i-1)/2;
    }
    private int Left(int i) {
        return 2*(i+1)-1;
    }
    private int Right(int i) {
        return 2*(i+1);
    }
}
在准备前端面试时,尤其是针对算法类题目,力扣(LeetCode)是一个非常重要的资源。以下是一些前端面试中常考的算法题目类型及具体示例,涵盖基础数据结构、排序、搜索、动态规划等多个方面。 ### 常见算法题型分类 #### 1. 数组与字符串操作 - **两数之和(Two Sum)**:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。 - **最长公共前缀(Longest Common Prefix)**:找出字符串数组中的最长公共前缀。 - **三数之和(3Sum)**:找出数组中所有满足和为零的三元组。 - **盛最多水的容器(Container With Most Water)**:给定 n 个非负整数,每个数对应坐标上的高度,找出两条线使得它们与 x 轴共同构成的容器可以容纳最多的水 [^2]。 #### 2. 排序与查找 - **合并两个有序数组(Merge Sorted Array)**:将两个有序数组合并为一个有序数组。 - **搜索旋转排序数组(Search in Rotated Sorted Array)**:在一个旋转后的有序数组中查找目标值。 - **寻找重复数(Find the Duplicate Number)**:在长度为 n + 1 的数组中找出唯一重复的那个数字。 #### 3. 递归与回溯 - **子集(Subsets)**:返回一个数组的所有子集 [^1]。 - **全排列(Permutations)**:返回一个数组的所有全排列。 - **组合总和(Combination Sum)**:找出所有和为目标值的组合。 - **N皇后(N-Queens)**:在 N×N 棋盘上放置 N 个皇后,使得它们互不攻击。 #### 4. 动态规划 - **最大子序和(Maximum Subarray)**:找出一个连续子数组使其和最大。 - **爬楼梯(Climbing Stairs)**:每次可以爬 1 或 2 阶楼梯,问有多少种不同的方法到达楼顶。 - **不同路径(Unique Paths)**:机器人从左上角到右下角有多少种不同的路径。 - **编辑距离(Edit Distance)**:计算两个字符串之间的最小编辑操作次数。 #### 5. 树与图 - **二叉树的前序、中序、后序遍历(Binary Tree Traversal)**:实现递归与非递归版本。 - **验证二叉搜索树(Validate Binary Search Tree)**:判断一棵树是否是二叉搜索树。 - **二叉树的最大深度(Maximum Depth of Binary Tree)**:求二叉树的最大深度。 - **岛屿数量(Number of Islands)**:在一个二维网格中,'1' 表示陆地,'0' 表示水域,求岛屿的数量。 #### 6. 其他高频题型 - **有效的括号(Valid Parentheses)**:判断括号字符串是否合法。 - **环形链表(Linked List Cycle)**:判断链表中是否有环。 - **LRU 缓存机制(LRU Cache)**:设计一个支持 get 和 put 操作的最近最少使用缓存。 - **接雨水(Trapping Rain Water)**:计算可以接多少单位的雨水 [^2]。 ### 示例代码:子集生成 ```javascript var subsets = function(nums) { let n = 1 << nums.length; let res = []; for(let i = 0; i < n; i++) { let temp = []; for(let j = 0; j < nums.length; j++) { if(i & (1 << j)) { temp.push(nums[j]); } } res.push(temp); } return res; }; ``` ### 示例代码:全排列 ```javascript function permute(nums) { const result = []; const used = new Array(nums.length).fill(false); function dfs(path) { if (path.length === nums.length) { result.push([...path]); return; } for (let i = 0; i < nums.length; i++) { if (used[i]) continue; used[i] = true; path.push(nums[i]); dfs(path); path.pop(); used[i] = false; } } dfs([]); return result; } ``` ### 总结 在准备前端面试时,算法题的核心在于理解问题本质、掌握常见解题思路、熟悉常用数据结构,并能灵活运用递归、回溯、动态规划等技巧。力扣上的上述题目是高频考点,建议反复练习并尝试多种解法。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值