链表排序 感悟及源码
来源:互联网 发布:vr 知乎 编辑:程序博客网 时间:2024/06/05 05:18
最近在补充和提高数据结构及算法方面的知识,对于链表操作有些感悟,特此记录下来。
一般的链表生成、插入、去除等基本操作没什么好说的,只是在做两个有序链表合成一个的题目时感觉有些吃力。花了很长时间外加调试终于磕磕碰碰把它做完后,问了自己一个问题是,如果原始链表是乱序的呢? 那就先排序呗。 没想到的是,链表排序比数组排序要复杂N倍。 指针飞来飞去的,很容易把脑子搞糊涂。虽然最后独自完成了,但过程蛮痛苦,也发现自己对于链表的复杂操作比较薄弱,缺乏练习。打击人啊 :)
总结几点感悟:
1)链表是数据结构基础中的基础,尤其在复杂些的非线性数据结构中,如树,图, 基本上都是写链表操作。 所以多花些力气把链表操作尤其是复杂操作练熟还是很有帮助的。我以前就对它不是很重视,总以为应用很少。。。
2) 无论是基本操作还是复制操作,都要动手去实现它。 试问自己一下,能不看任何资料、准确无误快速的写一个链表生成的代码吗?起码我以前是不行,这东西只有多练过几次才熟练。
3)在链表基本操作中,如删除或添加某节点, 我们总是先从Head节点for循环遍历到目标节点的前继节点,然后在进行相应的操作。 但是在链表的sort或merge的复杂操作中, 需要定义额外的指针来实时记录目标节点的前继节点。 所以在这类操作中,对于单个链表,往往定义额外的4、5指针,有的用于遍历原始链表、有的用于实时记录前继节点、有的用于比较节点值大小。。。
4)在链表排序中,一般地有两种思路,一是不停的遍历原始链表找出最大或最小值的节点,然后remove再依次插到新的链表中;另一是从头结点开始遍历链表,将找到的最大或最小的节点和头节点交换,然后从第二个节点开始遍历找到第二个最大值,并和第二个节点进行交换,依次类推。。。 从这里可以看出,这两种思路的基础是 选择排序法,而不是bubble sort。
废话少说,附上算法实现源代码。欢迎指正。。。
第一种思路
static void RemoveNode(LINK_NODE *pFront, LINK_NODE *pRemove, LINK *ppLinkList)610 {611 if (pFront == pRemove)612 {613 *ppLinkList = (*ppLinkList)->pNextNode;614 }615 else616 {617 pFront->pNextNode = pRemove->pNextNode;618 }619 printf("remove value is %d \n", pRemove->index);620 }621 622 static void InsertNode(LINK *ppInsertFront, LINK_NODE *pInsert, LINK *ppNewList) 623 { 624 if (*ppInsertFront == NULL) 625 { 626 *ppNewList = pInsert; 627 *ppInsertFront = pInsert; 628 } 629 else 630 { 631 (*ppInsertFront)->pNextNode = pInsert; 632 *ppInsertFront = (*ppInsertFront)->pNextNode; 633 } 634 } 635 636 void SortLinkList(LINK_NODE *pList, LINK *ppNewList) 637 { 638 LINK_NODE *pOrignNode, *pNewNode; 639 LINK_NODE *pRemoveNode, *pFrontNode; 640 LINK_NODE *pInsertFront; 641 642 pOrignNode = pNewNode = pList; 643 pRemoveNode = pFrontNode = pOrignNode; 644 pInsertFront = NULL; 645 646 while (pOrignNode) 647 { 648 while (pOrignNode->pNextNode) 649 { 650 if (pOrignNode->pNextNode->index < pRemoveNode->index) 651 { 652 pFrontNode = pOrignNode; 653 pRemoveNode = pOrignNode->pNextNode; 654 } 655 pOrignNode = pOrignNode->pNextNode; 656 } 657 RemoveNode(pFrontNode, pRemoveNode, &pList); 658 InsertNode(&pInsertFront, pRemoveNode, ppNewList); 659 660 pOrignNode = pFrontNode = pList; 661 pRemoveNode = pOrignNode; 662 } 663 pInsertFront = NULL; 664 }
第二种思路
static void SwitchNode(LINK_NODE *pFront1, LINK_NODE *pNode1, LINK_NODE *pFront2, LINK_NODE *pNode2, LINK *ppNewList) 667 { 668 LINK_NODE *pTempNode; 669 670 if (pFront1 == pFront2) 671 return; 672 673 if (pNode1 == pFront2) 674 { 675 pFront1->pNextNode = pNode2; 676 pTempNode = pNode2->pNextNode; 677 pNode2->pNextNode = pNode1; 678 pNode1->pNextNode = pTempNode; 679 return; 680 } 681 682 if (pFront1 == pNode1) 683 { 684 *ppNewList = pNode2; 685 } 686 else 687 { 688 pFront1->pNextNode = pNode2; 689 } 690 691 pTempNode = pNode2->pNextNode; 692 pNode2->pNextNode = pNode1->pNextNode; 693 694 pFront2->pNextNode = pNode1; 695 pNode1->pNextNode = pTempNode; 696 } 697 698 void SelectSortList(LINK_NODE *pList, LINK *ppNewList) 699 { 700 LINK_NODE *pNode1, *pNode2; 701 LINK_NODE *pFront1, *pFront2; 702 LINK_NODE *pMaxNode; 703 704 pNode1 = pNode2 = pList; 705 pFront1 = pFront2 = pList; 706 707 while (pNode1) 708 { 709 pMaxNode = pNode1; 710 while (pNode2->pNextNode) 711 { 712 if (pNode2->pNextNode->index > pMaxNode->index) 713 { 714 pFront2 = pNode2; 715 pMaxNode = pNode2->pNextNode; 716 } 717 pNode2 = pNode2->pNextNode; 718 } 719 SwitchNode(pFront1, pNode1, pFront2, pMaxNode, ppNewList); 720 pFront1 = pMaxNode; 721 pNode1 = pMaxNode->pNextNode; 722 pNode2 = pNode1; 723 } 724 }
- 链表排序 感悟及源码
- 读《STL 源码剖析》及感悟
- Redis源码学习感悟
- TinyHttpd源码感悟
- 常用排序算法总结及源码
- 快速排序qsort()源码及使用实例。
- 排序算法源码及效率分析汇总
- 【分治法】合并排序及C++源码
- 常见几个排序源码及二分查找源码
- 交换排序之冒泡排序优化源码及时间复杂度
- 插入排序之shell排序分析及源码演示
- 开发及生活感悟
- [转]阅读源码的感悟
- 源码分析的一点感悟
- AtomicIntegerArray源码分析与感悟
- 链表的快速排序及冒泡排序
- 基于链表的快速排序及归并排序
- 循环双向链表及快速排序
- Winform 统一捕获异常,捕获未处理异常
- 从excel中读取数据
- 黑马程序员_泛型
- HTTP返回值含义
- H.264 授权费用
- 链表排序 感悟及源码
- K-Means聚类算法详解
- spring中commons-logging.jar的作用
- source xxx.sh脚本 与 ./xxx.sh脚本的区别
- .NET学习
- HDU1999 不可摸数 【数论】
- CvMat操作
- mfc的类型转换
- 最小生成树之 Kruskal