C# 第k个最小元素(K’th Smallest Element)

目录

[【朴素方法】使用排序------时间复杂度为 O(n log(n)),空间复杂度为 O(1)](#【朴素方法】使用排序——时间复杂度为 O(n log(n)),空间复杂度为 O(1))

[【预期方法】使用最大堆 - 时间复杂度为 O(n * log(k)),空间复杂度为 O(k)](#【预期方法】使用最大堆 - 时间复杂度为 O(n * log(k)),空间复杂度为 O(k))

[【替代方案 1】使用快速选择](#【替代方案 1】使用快速选择)

[【替代方案 2】使用计数排序](#【替代方案 2】使用计数排序)


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

给定一个整数数组arr\[\]和元素个数k,求数组中第 k 小的元素。

注意: k 始终小于数组的大小。

例如:

输入:arr\[\] = 10, 5, 4, 3, 48, 6, 2, 33, 53, 10, k = 4

输出:5

说明:给定数组中第四小的元素是 5。

输入:arr\[\] = 7, 10, 4, 3, 20, 15, k = 3

输出:7

说明:给定数组中第三小的元素是 7。

【朴素方法】使用排序------时间复杂度为 O(n log(n)),空间复杂度为 O(1)

其思路是对给定的数组进行排序,并返回索引 k - 1 处的元素。

using System;

class GFG

{

static int kthSmallest(int\[\] arr, int k)

{

// Sort the given array

Array.Sort(arr);

// Return k'th element in the sorted array

return arrk - 1;

}

static void Main()

{

int\[\] arr = {10, 5, 4, 3, 48, 6, 2, 33, 53, 10};

int k = 4;

Console.WriteLine(kthSmallest(arr, k));

}

}

输出

5

【预期方法】使用最大堆 - 时间复杂度为 O(n * log(k)),空间复杂度为 O(k)

其思路是在遍历数组的过程中维护一个大小为 k 的最大堆。该堆始终包含目前为止遇到的 k 个最小元素。如果堆的大小超过 k,则移除最大的元素。最终,堆中只保留 k 个最小元素。

using System;

class MaxHeap

{

private int\[\] heap;

private int size;

public MaxHeap(int capacity)

{

heap = new intcapacity;

size = 0;

}

public int Count => size;

public void Push(int val)

{

heapsize = val;

int i = size;

size++;

while (i > 0)

{

int parent = (i - 1) / 2;

if (heapparent >= heapi)

break;

int temp = heapparent;

heapparent = heapi;

heapi = temp;

i = parent;

}

}

public int Pop()

{

if (size == 0)

{

Console.WriteLine("Heap is empty");

return -1;

}

int top = heap0;

heap0 = heapsize - 1;

size--;

int i = 0;

while (true)

{

int left = 2 * i + 1;

int right = 2 * i + 2;

int largest = i;

if (left < size && heapleft > heaplargest)

largest = left;

if (right < size && heapright > heaplargest)

largest = right;

if (largest == i)

break;

int temp = heapi;

heapi = heaplargest;

heaplargest = temp;

i = largest;

}

return top;

}

public int Top()

{

if (size == 0)

{

Console.WriteLine("Heap is empty");

return -1;

}

return heap0;

}

}

class GFG {

static int kthSmallest(int\[\] arr, int k)

{

// Create a max heap

MaxHeap pq = new MaxHeap(arr.Length);

// Iterate through the array elements

for (int i = 0; i < arr.Length; i++)

{

// Push the current element onto the max heap

pq.Push(arri);

// If the size of the max heap exceeds k,

//remove the largest element

if (pq.Count > k)

pq.Pop();

}

// Return the kth smallest element (top of the max heap)

return pq.Top();

}

static void Main()

{

int\[\] arr = {10, 5, 4, 3, 48, 6, 2, 33, 53, 10};

int k = 4;

Console.WriteLine(kthSmallest(arr, k));

}

}

输出

5

【替代方案 1】使用快速选择

主要思路是利用快速选择(QuickSelect)函数找到第 k 大元素。具体做法是:选择一个基准元素,然后将数组分割成多个部分,使得大于基准元素的元素位于左侧,小于基准元素的元素位于右侧。如果基准元素最终位于索引 k-1 处,则该元素即为第 k 大元素。否则,我们递归地仅在包含第 k 大元素的左侧或右侧部分进行搜索。

using System;

class GFG {

static int partition(int\[\] arr, int left, int right) {

// Choose the last element as pivot

int pivot = arrright;

int i = left;

// Traverse the array and move elements <= pivot to the left

for (int j = left; j < right; j++) {

if (arrj <= pivot) {

// Swap current element with element at i

int temp = arri;

arri = arrj;

arrj = temp;

i++;

}

}

// Place the pivot in its correct position

int tempPivot = arri;

arri = arrright;

arrright = tempPivot;

return i;

}

static int quickSelect(int\[\] arr, int left, int right, int k) {

if (left <= right) {

// Partition around pivot

int pivotIndex = partition(arr, left, right);

// Found k-th smallest

if (pivotIndex == k)

return arrpivotIndex;

else if (pivotIndex > k)

return quickSelect(arr, left, pivotIndex - 1, k);

else

return quickSelect(arr, pivotIndex + 1, right, k);

}

return -1;

}

static int kthSmallest(int\[\] arr, int k) {

return quickSelect(arr, 0, arr.Length - 1, k - 1);

}

static void Main() {

int\[\] arr = {10, 5, 4, 3, 48, 6, 2, 33, 53, 10};

int k = 4;

Console.WriteLine(kthSmallest(arr, k));

}

}

输出

5

时间复杂度: 最坏情况下为O(n² ),但平均时间为 O(n log n),且性能优于基于优先级队列的算法。

辅助空间: 最坏情况下递归调用栈为 O(n)。平均而言:O(log n)。

【替代方案 2】使用计数排序

主要思路是利用计数排序的频率计数来跟踪有多少元素小于或等于每个值,然后直接从这些累积计数中识别出第 K 小的元素,而无需对数组进行完全排序。

注意:这种方法在元素范围较小时特别有效,因为我们声明的数组大小为最大元素个数。如果元素范围非常大,计数排序方法可能并非最有效的选择。

using System;

class GFG {

static int kthSmallest(int\[\] arr, int k)

{

// First, find the maximum element in the array

int maxElement = arr0;

for (int i = 1; i < arr.Length; i++)

{

if (arri > maxElement)

maxElement = arri;

}

// Create a frequency array for each element

int\[\] freq = new intmaxElement + 1;

for (int i = 0; i < arr.Length; i++)

freqarr\[i]++;

// Keep track of cumulative frequency to find k-th smallest

int count = 0;

for (int i = 0; i <= maxElement; i++)

{

if (freqi != 0)

{

count += freqi;

if (count >= k)

{

// If we have seen k or more elements,

// return the current element

return i;

}

}

}

return -1;

}

static void Main()

{

int\[\] arr = {10, 5, 4, 3, 48, 6, 2, 33, 53, 10};

int k = 4;

Console.WriteLine(kthSmallest(arr, k));

}

}

输出

5

时间复杂度: O(n + maxElement),其中 maxElement 为数组中的最大元素。

辅助空间: O(maxElement)。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关推荐
BioRunYiXue1 小时前
RACE技术全攻略:从全长克隆到靶基因验证
java·javascript·网络·人工智能·科技·算法·eclipse
影寂ldy1 小时前
Modbus-ASCII 协议 + LRC校验
笔记·网络协议·c#
ZPC82101 小时前
joy 及sensor_msgs
算法
Three_ST1 小时前
沐神-动手学习深度学习-习题答案4.4模型选择,欠拟合,过拟合
人工智能·python·深度学习·学习·算法
CIO_Alliance1 小时前
AI深度系列(2)| CNN卷积池化感受野原理:从局部感知到全局视野
人工智能·深度学习·线性代数·算法·计算机视觉·企业ai转型·企业cio联盟
探物 AI2 小时前
机器人清理墙角蜘蛛网,背后需要哪些算法?——从视觉分割到贴墙力控
算法·机器人
自己的九又四分之三站台2 小时前
C# 接入 RasterLite2:从原生 DLL 加载到 Coverage、Section、Tile 验证
开发语言·c#·地理信息
青 春 记 忆2 小时前
LeetCode 226. 翻转二叉树|Python 解法详解
python·算法·leetcode
民乐团扒谱机2 小时前
【微科普】压缩感知(CS):违反了奈奎斯特采样定理?不先采集也能还原信号?大白话讲透稀疏采样、L1重建与OMP,一文吃透附代码
python·神经网络·线性代数·算法·数学建模·压缩感知·奈奎斯特