- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
来自 http://www.geeksforgeeks.org/merge-sort-for-linked-list/
The slow random-access performance of a linked list makes some other algorithms (such as quicksort) perform poorly, and others (such as heapsort) completely impossible.
但是,我真的不明白为什么在对链表进行排序时快速排序的性能会比归并排序差。
在快速排序中:
选择一个枢轴需要随机访问,并且需要遍历链表(每次递归 O(n))。
分区可以使用从左到右的扫描方式完成(不需要随机访问):
在合并排序中:
中间拆分需要随机访问,并且需要遍历链表(使用快慢指针机制)(每次递归 O(n))。
合并可以通过从左到右的扫描方式完成(不需要随机访问)。
据我所知,快速排序和合并排序都需要在每次递归中进行随机访问,而且由于链表的非随机访问性质,我不明白为什么快速排序的性能会比合并排序差。
我是不是漏掉了什么?
编辑:我正在查看分区函数,其中 pivot 是最后一个元素,我们按顺序从 lwft 开始扫描。如果分区的工作方式不同(即枢轴位于中间,并且您在每一端维护两个指针),如果链表是双向链接的,它仍然可以正常工作...
最佳答案
我正在更新此答案以提供更好的比较。在我下面的原始答案中,我包含了一个自底向上合并排序的示例,它使用了一个指向列表的小数组指针。 merge 函数将两个列表合并为一个目标列表。作为替代方案,合并功能可以通过拼接操作将一个列表合并到另一个列表中,这意味着对于伪随机数据,更新链接的时间只有大约一半。对于数组,合并排序比快速排序执行更多的移动但更少的比较,但是如果链表合并是将一个列表合并到另一个列表,则“移动”的数量将减少一半。
对于快速排序,第一个节点可以用作枢轴,只有小于枢轴的节点才会被移动,形成一个在枢轴之前的列表(以相反的顺序),这也意味着只更新大约一半的链接伪随机数据的时间。
快速排序的问题在于分区并不完美,即使使用伪随机数据也是如此,而归并排序(自上而下或自下而上)相当于完美分区。快速排序的常见分析考虑了通过各种选择枢轴的方式,枢轴落在列表中间 75% 的概率,对于 75%/25% 的拆分(相对于合并排序总是得到 50%/50% 的拆分)。我比较了第一个节点作为枢轴的快速排序与具有 400 万个 64 位伪随机整数的合并排序,快速排序花费了 45% 的时间,增加了 30% 的拼接操作(链接更新或节点“移动”)和其他开销。
原始答案
对于链表,有一个自下而上的迭代合并排序版本,它不扫描列表来拆分它们,这避免了随机访问性能慢的问题。链表的自下而上合并排序使用一个小的(25 到 32)节点指针数组。时间复杂度为 O(n log(n)),空间复杂度为 O(1)(指向节点的 25 到 32 指针的数组)。
在那个网页
http://www.geeksforgeeks.org/merge-sort-for-linked-list
我已经发布了一些评论,包括指向链表自下而上合并排序工作示例的链接,但从未收到该组的回复。链接到用于该网站的工作示例:
http://code.geeksforgeeks.org/Mcr1Bf
对于没有随机访问的快速排序,第一个节点可以用作枢轴。将创建三个列表,一个列表用于节点 < pivot,一个列表用于节点 == pivot,一个列表用于节点 > pivot。递归将用于节点 != pivot 的两个列表。这具有 O(n^2) 的最坏情况时间复杂度和 O(n) 的最坏情况堆栈空间复杂度。堆栈空间复杂度可以降低到 O(log(n)),方法是仅在节点 != pivot 的较短列表上使用递归,然后循环返回以使用较长列表的第一个节点作为新的 pivot 对较长列表进行排序.跟踪列表中的最后一个节点(例如使用指向循环列表的尾指针)将允许快速连接其他两个列表。最坏情况下的时间复杂度保持在 O(n^2)。
需要指出的是,如果有空间,将链表移动到数组(或向量)、对数组进行排序并从排序后的数组创建新的排序列表通常要快得多。
示例 C 代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct NODE_{
struct NODE_ * next;
int data;
}NODE;
/* merge two already sorted lists */
/* compare uses pSrc2 < pSrc1 to follow the STL rule */
/* of only using < and not <= */
NODE * MergeLists(NODE *pSrc1, NODE *pSrc2)
{
NODE *pDst = NULL; /* destination head ptr */
NODE **ppDst = &pDst; /* ptr to head or prev->next */
if(pSrc1 == NULL)
return pSrc2;
if(pSrc2 == NULL)
return pSrc1;
while(1){
if(pSrc2->data < pSrc1->data){ /* if src2 < src1 */
*ppDst = pSrc2;
pSrc2 = *(ppDst = &(pSrc2->next));
if(pSrc2 == NULL){
*ppDst = pSrc1;
break;
}
} else { /* src1 <= src2 */
*ppDst = pSrc1;
pSrc1 = *(ppDst = &(pSrc1->next));
if(pSrc1 == NULL){
*ppDst = pSrc2;
break;
}
}
}
return pDst;
}
/* sort a list using array of pointers to list */
/* aList[i] == NULL or ptr to list with 2^i nodes */
#define NUMLISTS 32 /* number of lists */
NODE * SortList(NODE *pList)
{
NODE * aList[NUMLISTS]; /* array of lists */
NODE * pNode;
NODE * pNext;
int i;
if(pList == NULL) /* check for empty list */
return NULL;
for(i = 0; i < NUMLISTS; i++) /* init array */
aList[i] = NULL;
pNode = pList; /* merge nodes into array */
while(pNode != NULL){
pNext = pNode->next;
pNode->next = NULL;
for(i = 0; (i < NUMLISTS) && (aList[i] != NULL); i++){
pNode = MergeLists(aList[i], pNode);
aList[i] = NULL;
}
if(i == NUMLISTS) /* don't go beyond end of array */
i--;
aList[i] = pNode;
pNode = pNext;
}
pNode = NULL; /* merge array into one list */
for(i = 0; i < NUMLISTS; i++)
pNode = MergeLists(aList[i], pNode);
return pNode;
}
/* allocate memory for a list */
/* create list of nodes with pseudo-random data */
NODE * CreateList(int count)
{
NODE *pList;
NODE *pNode;
int i;
int r;
/* allocate nodes */
pList = (NODE *)malloc(count * sizeof(NODE));
if(pList == NULL)
return NULL;
pNode = pList; /* init nodes */
for(i = 0; i < count; i++){
r = (((int)((rand()>>4) & 0xff))<< 0);
r += (((int)((rand()>>4) & 0xff))<< 8);
r += (((int)((rand()>>4) & 0xff))<<16);
r += (((int)((rand()>>4) & 0x7f))<<24);
pNode->data = r;
pNode->next = pNode+1;
pNode++;
}
(--pNode)->next = NULL;
return pList;
}
#define NUMNODES (1024) /* number of nodes */
int main(void)
{
void *pMem; /* ptr to allocated memory */
NODE *pList; /* ptr to list */
NODE *pNode;
int data;
/* allocate memory and create list */
if(NULL == (pList = CreateList(NUMNODES)))
return(0);
pMem = pList; /* save ptr to mem */
pList = SortList(pList); /* sort the list */
data = pList->data; /* check the sort */
while(pList = pList->next){
if(data > pList->data){
printf("failed\n");
break;
}
data = pList->data;
}
if(pList == NULL)
printf("passed\n");
free(pMem); /* free memory */
return(0);
}
关于algorithm - 由于链表中没有随机访问,使用快速排序对链表进行排序真的比合并排序慢吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41771356/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!