382. Linked List Random Node

题目:

Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen.

Follow up:
What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space?

Example:

// Init a singly linked list [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);

// getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.
solution.getRandom();

 

思路1:

常规思路遍历整个list,记录下长度然后生成随机数来确定选哪个Node,遍历到那个Node即可。但是要对list进行两次遍历。

 

代码1:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    Solution(ListNode* head) {
        root=head;
        while(head)
        {
            size++;
            head=head->next;
        }
    }
    
    /** Returns a random node's value. */
    int getRandom() {
        ListNode* temp=root;
        int index=rand()%size;
        while(index)
        {
            index--;
            temp=temp->next;
        }
        return temp->val;
    }
private:
    ListNode* root;
    int size=0;
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* obj = new Solution(head);
 * int param_1 = obj->getRandom();
 */

 

思路2:

Follow up中问了如果list很长无法知道length怎么办。这里使用蓄水池算法,其实是之前一种shuffle方式的升级,本质是能够从n个数中随机取出m个数,步骤如下:

首先构建一个m大小的数组,这就是最初的池子,把list中的前m个数放入数组。

建立一个整数ran,大小为m+1,生成随机数[0, ran]之间,如果随机数在[0, m-1]内,即在池子内,就把当前数和池内对应数swap。

每一轮都增大ran,并且node往下遍历,直到整个list遍历结束。

假设池子大小为1,[a],然后下一个是b。那么只要保证50%是a,50%是b即可。

假设池子大小为2,[a,b],然后下一个是c。那么只要保证a,b,c都是1/3。即池子的概率是2/3,下一个数概率是1/3。

假设池子大小为2,[a,b,c],然后下一个是d。那么只要保证a,b,c,d都是1/4。即池子的概率是3/4,下一个数概率是1/4。

这题就是池子大小为1时的情况,因此只要用一个整数来替代池子即可。如果随机数为0,则把当前数和池子中的数交换。

 

代码2:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    Solution(ListNode* head) {
        root=head;
    }
    
    /** Returns a random node's value. */
    int getRandom() {
        ListNode* temp=root;
        int pool=temp->val;
        temp=temp->next;
        for(int ran=2; temp;ran++)
        {
            if(rand()%ran==0)
                pool=temp->val;
            temp=temp->next;
        }
        return pool;
    }
private:
    ListNode* root;
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* obj = new Solution(head);
 * int param_1 = obj->getRandom();
 */

Reference: https://xujimmy.com/2017/10/15/shuffle.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,以下是回复: 首先,我们可以使用Python语言来实现这个功能。具体步骤如下: 1. 导入random模块,用于生成随机数。 2. 定义一个链表类,包含节点类和链表类。 3. 定义一个函数,用于生成100个随机整数并存入链表。 4. 定义一个函数,用于将链表分为两个,一个存放所有负整数,另一个存放所有非负整数。 5. 输出三个链表,分别为原链表、负整数链表和非负整数链表。 具体代码如下: ```python import random # 节点类 class Node: def __init__(self, data): self.data = data self.next = None # 链表类 class LinkedList: def __init__(self): self.head = None # 在链表末尾添加节点 def append(self, data): new_node = Node(data) if self.head is None: self.head = new_node return last_node = self.head while last_node.next: last_node = last_node.next last_node.next = new_node # 输出链表 def print_list(self): curr_node = self.head while curr_node: print(curr_node.data, end=' ') curr_node = curr_node.next print() # 生成100个随机整数并存入链表 def generate_list(): linked_list = LinkedList() for i in range(100): linked_list.append(random.randint(-100, 100)) return linked_list # 将链表分为两个,一个存放所有负整数,另一个存放所有非负整数 def split_list(linked_list): negative_list = LinkedList() non_negative_list = LinkedList() curr_node = linked_list.head while curr_node: if curr_node.data < : negative_list.append(curr_node.data) else: non_negative_list.append(curr_node.data) curr_node = curr_node.next return negative_list, non_negative_list # 测试代码 if __name__ == '__main__': linked_list = generate_list() print('原链表:') linked_list.print_list() negative_list, non_negative_list = split_list(linked_list) print('负整数链表:') negative_list.print_list() print('非负整数链表:') non_negative_list.print_list() ``` 希望能对您有所帮助! ### 回答2: 将100个随机整数存储到链表中,我们可以定义一个链表节点结构体,包含一个整数类型的value和指向下一个节点的指针。然后从-100到100遍历100次,每次生成一个随机数,并将其加入链表中。 以下是示例代码: ```c++ #include <iostream> #include <cstdlib> #include <ctime> using namespace std; struct ListNode { int value; ListNode *next; ListNode(int value) : value(value), next(NULL) {} }; ListNode *createList() { srand(time(NULL)); ListNode *head = new ListNode(0); ListNode *tail = head; for (int i = 0; i < 100; i++) { int value = rand() % 201 - 100; ListNode *node = new ListNode(value); tail->next = node; tail = node; } return head->next; } void printList(ListNode *head) { while (head != NULL) { cout << head->value << " "; head = head->next; } cout << endl; } ListNode *splitList(ListNode *head) { ListNode *positiveHead = new ListNode(0); ListNode *positiveTail = positiveHead; ListNode *negativeHead = new ListNode(0); ListNode *negativeTail = negativeHead; while (head != NULL) { if (head->value >= 0) { positiveTail->next = head; positiveTail = head; } else { negativeTail->next = head; negativeTail = head; } head = head->next; } positiveTail->next = NULL; negativeTail->next = NULL; return make_pair(positiveHead->next, negativeHead->next); } int main() { ListNode *head = createList(); cout << "随机生成的链表为:"; printList(head); pair<ListNode *, ListNode *> result = splitList(head); cout << "正整数链表为:"; printList(result.first); cout << "负整数链表为:"; printList(result.second); return 0; } ``` 在这个代码中,我们生成了100个随机整数,并将它们存储到一个链表中。然后我们使用splitList函数将链表分成了两个链表,一个存储正整数,另一个存储负整数。最后我们输出了这两个链表。 ### 回答3: 本题要求的是随机生成100个整数,再将它们按照正负分为两个链表输出。因为需要使用链表来存储生成的数字,所以我们需要首先定义链表结构体,其具体实现如下: ```c typedef struct Node{ int data; struct Node* next; }Node, *LinkList; ``` 定义好链表结构体之后,我们需要计算随机生成100个整数的具体实现过程。由于本题所要求的整数范围在[-100,100]之间,因此,我们可以使用rand()函数生成一个在0到199之间的整数,然后将其减去100便可得到[-100,100]之间的整数。因此,生成100个整数的具体实现过程如下: ```c srand(time(NULL)); LinkList head = (LinkList)malloc(sizeof(Node)); head->next = NULL; int i = 0; for (i = 0; i < 100; i++){ int data = rand() % 200 - 100; Node* p = (Node*)malloc(sizeof(Node)); p->data = data; p->next = head->next; head->next = p; } ``` 上述代码中,我们利用rand函数生成了100个在[-100, 100]之间的随机整数,然后将这些整数保存到一个单向链表中,链表头为head。通过这个链表,我们可以遍历所有的生成的整数,并分别输出它们。具体的代码实现过程如下: ```c void printList(LinkList head){ Node* p = head->next; while (p != NULL){ printf("%d ", p->data); p = p->next; } } ``` 接下来,我们需要将链表中的整数根据它们的正负分成两个链表。本题中,我们可以使用两个指针分别遍历整个链表,当指针所指向的整数为负数时,将其赋值到一个新的链表中。最终,我们就可以得到两个链表,一个存放所有负整数,另一个存放所有非负整数。具体代码实现如下: ```c LinkList negative(LinkList head){ LinkList p = head->next; LinkList negHead = (LinkList)malloc(sizeof(Node)); negHead->next = NULL; while (p != NULL){ if (p->data < 0){ Node* q = (Node*)malloc(sizeof(Node)); q->data = p->data; q->next = negHead->next; negHead->next = q; } p = p->next; } return negHead->next; } LinkList nonNegative(LinkList head){ LinkList p = head->next; LinkList nonNegHead = (LinkList)malloc(sizeof(Node)); nonNegHead->next = NULL; while (p != NULL){ if (p->data >= 0){ Node* q = (Node*)malloc(sizeof(Node)); q->data = p->data; q->next = nonNegHead->next; nonNegHead->next = q; } p = p->next; } return nonNegHead->next; } ``` 最后,我们需要输出这两个分别存储所有负整数和非负整数的链表。当然,我们还是需要使用printList函数输出整数,只不过这次,我们需要对两个链表调用printList函数。这里不再赘述。 综上所述,随机生成100个整数存入链表,再将链表分为两个以存储正数和负数,输出这两个链表,是一道比较典型的链表操作题目。除了需要熟悉链表的定义、创建和输出之外,我们还需要使用原理较为简单的随机数生成算法,并对正负数分别进行分类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值