IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 常数时间删除/查找数组中的任意元素 -> 正文阅读

[数据结构与算法]常数时间删除/查找数组中的任意元素

1时间插入、删除和获取随机元素

?

这些问题的一个技巧点在于,如何结合哈希表和数组,使得数组的删除操作时间复杂度也变成 O(1)?下面来一道道看。

代码如下:

class RandomizedSet {
   
    List<Integer> list=new ArrayList<>();
         Map<Integer,Integer> map= new HashMap<>();
      Random random=   new Random();
       public RandomizedSet() {
       }

       public boolean insert(int val) {
           if (map.containsKey(val)){
               return false;
           }
           list.add(val);
           map.put(val,list.size()-1);
           return true;
       }

       public boolean remove(int val) {
           if (!map.containsKey(val)){
               return false;
           }
           Integer index = map.get(val);
           Integer last = list.get(list.size() - 1);
           list.set(index,last);

           list.remove(list.size()-1);

           map.put(last,index);
           map.remove(val);
           return true;

       }

       public int getRandom() {
        return   list.get(random.nextInt(list.size()));
       }
   
}

2避开黑名单的随机数

给你输入一个正整数?N,代表左闭右开区间?[0,N),再给你输入一个数组?blacklist,其中包含一些「黑名单数字」,且?blacklist?中的数字都是区间?[0,N)?中的数字。

pick?函数会被多次调用,每次调用都要在区间?[0,N)?中「等概率随机」返回一个「不在?blacklist?中」的整数。

这应该不难理解吧,比如给你输入?N = 5, blacklist = [1,3],那么多次调用?pick?函数,会等概率随机返回 0, 2, 4 中的某一个数字。

而且题目要求,在?pick?函数中应该尽可能少调用随机数生成函数?rand()

这句话什么意思呢,比如说我们可能想出如下拍脑袋的解法:

int pick() {
    int res = rand() % N;
    while (res exists in blacklist) {
        // 重新随机一个结果
        res = rand() % N;
    }
    return res;
}

这个函数会多次调用?rand()?函数,执行效率竟然和随机数相关,不是一个漂亮的解法。

聪明的解法类似上一道题,我们可以将区间?[0,N)?看做一个数组,然后将?blacklist?中的元素移到数组的最末尾,同时用一个哈希表进行映射

根据这个思路,我们可以写出第一版代码(还存在几处错误):

class Solution {
public:
    int sz;
    unordered_map<int, int> mapping;
    
    Solution(int N, vector<int>& blacklist) {
        // 最终数组中的元素个数
        sz = N - blacklist.size();
        // 最后一个元素的索引
        int last = N - 1;
        // 将黑名单中的索引换到最后去
        for (int b : blacklist) {
            mapping[b] = last;
            last--;
        }
    }
};

如上图,相当于把黑名单中的数字都交换到了区间?[sz, N)?中,同时把?[0, sz)?中的黑名单数字映射到了正常数字。

根据这个逻辑,我们可以写出?pick?函数:

int pick() {
    // 随机选取一个索引
    int index = rand() % sz;
    // 这个索引命中了黑名单,
    // 需要被映射到其他位置
    if (mapping.count(index)) {
        return mapping[index];
    }
    // 若没命中黑名单,则直接返回
    return index;
}

这个?pick?函数已经没有问题了,但是构造函数还有两个问题。

第一个问题,如下这段代码:

int last = N - 1;
// 将黑名单中的索引换到最后去
for (int b : blacklist) {
    mapping[b] = last;
    last--;
}

我们将黑名单中的?b?映射到?last,但是我们能确定?last?不在?blacklist?中吗?

比如下图这种情况,我们的预期应该是 1 映射到 3,但是错误地映射到 4:

?

在对?mapping[b]?赋值时,要保证?last?一定不在?blacklist?中,可以如下操作:

// 构造函数
Solution(int N, vector<int>& blacklist) {
    sz = N - blacklist.size();
    // 先将所有黑名单数字加入 map
    for (int b : blacklist) { 
        // 这里赋值多少都可以
        // 目的仅仅是把键存进哈希表
        // 方便快速判断数字是否在黑名单内
        mapping[b] = 666;
    }

    int last = N - 1;
    for (int b : blacklist) {
        // 跳过所有黑名单中的数字
        while (mapping.count(last)) {
            last--;
        }
        // 将黑名单中的索引映射到合法数字
        mapping[b] = last;
        last--;
    }
}

第二个问题,如果?blacklist?中的黑名单数字本身就存在区间?[sz, N)?中,那么就没必要在?mapping?中建立映射,比如这种情况:

我们根本不用管 4,只希望把 1 映射到 3,但是按照?blacklist?的顺序,会把 4 映射到 3,显然是错误的。

我们可以稍微修改一下,写出正确的解法代码:java版本

 class Solution {
        int sz;
        Map<Integer,Integer> map=new HashMap<>();
      Random r = new Random();

        public Solution(int n, int[] blacklist) {
          sz=n-blacklist.length;
            for (int i : blacklist) {
                map.put(i,666);
            }
            int last =blacklist.length-1;
            for (int i : blacklist) {
                if (i>=sz){
                    continue;
                }
                while (map.containsKey(last)){
                    last--;
                }
                map.put(i,last);
                last--;
            }

        }

        public int pick() {
            // 随机选取一个索引
            int i = r.nextInt(sz);

            return map.getOrDefault(i,i);
        }
    }

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-13 11:54:55  更:2022-05-13 11:56:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:33:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码