单链表排序方法


单链表排序是单链表的常见编程任务之一,也是面试中经常出现的题目。单链表排序的关键是交换算法,需要额外考虑。选择排序是比较直观的排序算法之一,这里就使用选择排序实现单链表的排序。

如果需要对选择排序复习一下,传送门:算法导论:选择排序的原理与实现

C实现代码如下:

01 LinkList SelectSort2(LinkList L)
02 {
03     LinkList p,q,small;
04     int temp;
05  
06     for(p = L->next; p->next != NULL; p = p->next)
07     {
08         small = p;
09         for(q = p->next; q; q = q->next)
10         {
11             if(q->data < small->data)
12             {
13                 small = q;
14             }
15         }
16         printf("循环后,获得最小值为:%d, 此时链表为:", small->data);
17         if(small != p)
18         {
19             temp = p->data;
20             p->data = small->data;
21             small->data = temp;
22         }
23         ListTraverse(L);
24     }
25     printf("输出排序后的数字:\n");
26     return L;
27 }

或者

01 LinkList SelectSort(LinkList L)
02 {
03     LinkList first;     /*排列后有序链的表头指针*/
04     LinkList tail;      /*排列后有序链的表尾指针*/
05     LinkList p_min;     /*保留键值更小的节点的前驱节点的指针*/
06     LinkList min;       /*存储最小节点*/
07     LinkList p;         /*当前比较的节点*/
08  
09     first = NULL;
10     while (L != NULL) /*在链表中找键值最小的节点。*/
11     {
12         /*注意:这里for语句就是体现选择排序思想的地方*/
13         for (p=L,min=L; p->next!=NULL; p=p->next) /*循环遍历链表中的节点,找出此时最小的节点。*/
14         {
15             if (p->next->data < min->data) /*找到一个比当前min小的节点。*/
16             {
17                 p_min = p; /*保存找到节点的前驱节点:显然p->next的前驱节点是p。*/
18                 min = p->next; /*保存键值更小的节点。*/
19             }
20         }
21  
22         /*上面for语句结束后,就要做两件事;
23             一是把它放入有序链表中;
24             二是根据相应的条件判断,安排它离开原来的链表。*/
25  
26         /*第一件事*/
27         if (first == NULL) /*如果有序链表目前还是一个空链表*/
28         {
29             first = min; /*第一次找到键值最小的节点。*/
30             tail = min; /*注意:尾指针让它指向最后的一个节点。*/
31         }
32         else /*有序链表中已经有节点*/
33         {
34             tail->next = min; /*把刚找到的最小节点放到最后,即让尾指针的next指向它。*/
35             tail = min; /*尾指针也要指向它。*/
36         }
37         /*第二件事*/
38         if (min == L) /*如果找到的最小节点就是第一个节点*/
39         {
40             //printf("表头%d已经是最小,当前结点后移。\n", min->data);
41             L = L->next; /*显然让head指向原head->next,即第二个节点,就OK*/
42         }
43         else /*如果不是第一个节点*/
44         {
45             p_min->next = min->next; /*前次最小节点的next指向当前min的next,这样就让min离开了原链表。*/
46         }
47     }
48     if (first != NULL) /*循环结束得到有序链表first*/
49     {
50         tail->next = NULL; /*单向链表的最后一个节点的next应该指向NULL*/
51     }
52     L = first;
53     return L;
54 }

完整的可执行程序:

001 #include "stdio.h"
002  
003 #define OK 1
004 #define ERROR 0
005 #define TRUE 1
006 #define FALSE 0
007  
008 typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
009 typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
010  
011 typedef struct Node
012 {
013     ElemType data;
014     struct Node *next;
015 } Node;
016 typedef struct Node *LinkList; /* 定义LinkList */
017  
018 Status visit(ElemType c)
019 {
020     printf("->%d",c);
021     return OK;
022 }
023  
024 /* 初始化顺序线性表 */
025 Status InitList(LinkList *L)
026 {
027     *L=(LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */
028     if(!(*L)) /* 存储分配失败 */
029         return ERROR;
030     (*L)->next=NULL; /* 指针域为空 */
031  
032     return OK;
033 }
034  
035 /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
036 int ListLength(LinkList L)
037 {
038     int i=0;
039     LinkList p=L->next; /* p指向第一个结点 */
040     while(p)
041     {
042         i++;
043         p=p->next;
044     }
045     return i;
046 }
047  
048 /* 初始条件:顺序线性表L已存在 */
049 /* 操作结果:依次对L的每个数据元素输出 */
050 Status ListTraverse(LinkList L)
051 {
052     LinkList p=L->next;
053     while(p)
054     {
055         visit(p->data);
056         p=p->next;
057     }
058     printf("\n");
059     return OK;
060 }
061  
062 /*  随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
063 void CreateListTail(LinkList *L, int n)
064 {
065     LinkList p,r;
066     int i;
067     srand(time(0));                      /* 初始化随机数种子 */
068     *L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
069     r=*L;                                /* r为指向尾部的结点 */
070     for (i=0; i < n; i++)
071     {
072         p = (Node *)malloc(sizeof(Node)); /*  生成新结点 */
073         p->data = rand()%100+1;           /*  随机生成100以内的数字 */
074         r->next=p;                        /* 将表尾终端结点的指针指向新结点 */
075         r = p;                            /* 将当前的新结点定义为表尾终端结点 */
076     }
077     r->next = NULL;                       /* 表示当前链表结束 */
078 }
079  
080 LinkList SelectSort2(LinkList L)
081 {
082     LinkList p,q,small;
083     int temp;
084  
085     for(p = L->next; p->next != NULL; p = p->next)
086     {
087         small = p;
088         for(q = p->next; q; q = q->next)
089         {
090             if(q->data < small->data)
091             {
092                 small = q;
093             }
094         }
095         printf("循环后,获得最小值为:%d, 此时链表为:", small->data);
096         if(small != p)
097         {
098             temp = p->data;
099             p->data = small->data;
100             small->data = temp;
101         }
102         ListTraverse(L);
103     }
104     printf("输出排序后的数字:\n");
105     return L;
106 }
107  
108 LinkList SelectSort(LinkList L)
109 {
110     LinkList first;     /*排列后有序链的表头指针*/
111     LinkList tail;      /*排列后有序链的表尾指针*/
112     LinkList p_min;     /*保留键值更小的节点的前驱节点的指针*/
113     LinkList min;       /*存储最小节点*/
114     LinkList p;         /*当前比较的节点*/
115  
116     first = NULL;
117     while (L != NULL) /*在链表中找键值最小的节点。*/
118     {
119         /*注意:这里for语句就是体现选择排序思想的地方*/
120         for (p=L,min=L; p->next!=NULL; p=p->next) /*循环遍历链表中的节点,找出此时最小的节点。*/
121         {
122             if (p->next->data < min->data) /*找到一个比当前min小的节点。*/
123             {
124                 p_min = p; /*保存找到节点的前驱节点:显然p->next的前驱节点是p。*/
125                 min = p->next; /*保存键值更小的节点。*/
126             }
127         }
128  
129         /*上面for语句结束后,就要做两件事;
130             一是把它放入有序链表中;
131             二是根据相应的条件判断,安排它离开原来的链表。*/
132  
133         /*第一件事*/
134         if (first == NULL) /*如果有序链表目前还是一个空链表*/
135         {
136             first = min; /*第一次找到键值最小的节点。*/
137             tail = min; /*注意:尾指针让它指向最后的一个节点。*/
138         }
139         else /*有序链表中已经有节点*/
140         {
141             tail->next = min; /*把刚找到的最小节点放到最后,即让尾指针的next指向它。*/
142             tail = min; /*尾指针也要指向它。*/
143         }
144         /*第二件事*/
145         if (min == L) /*如果找到的最小节点就是第一个节点*/
146         {
147             //printf("表头%d已经是最小,当前结点后移。\n", min->data);
148             L = L->next; /*显然让head指向原head->next,即第二个节点,就OK*/
149         }
150         else /*如果不是第一个节点*/
151         {
152             p_min->next = min->next; /*前次最小节点的next指向当前min的next,这样就让min离开了原链表。*/
153         }
154     }
155     if (first != NULL) /*循环结束得到有序链表first*/
156     {
157         tail->next = NULL; /*单向链表的最后一个节点的next应该指向NULL*/
158     }
159     L = first;
160     return L;
161 }
162  
163 int main()
164 {
165     LinkList L;
166     Status i;
167     char opp;
168     ElemType e;
169     int find;
170     int tmp;
171  
172     i=InitList(&L);
173     printf("初始化L后:ListLength(L)=%d\n",ListLength(L));
174  
175     printf("\n1.查看链表 \n2.创建链表(尾插法) \n3.链表长度 \n4.交换两个结点 \n0.退出 \n请选择你的操作:\n");
176     while(opp != '0')
177     {
178         scanf("%c",&opp);
179         switch(opp)
180         {
181         case '1':
182             ListTraverse(L);
183             printf("\n");
184             break;
185  
186         case '2':
187             CreateListTail(&L,10);
188             printf("整体创建L的元素(尾插法):\n");
189             ListTraverse(L);
190             printf("\n");
191             break;
192  
193         case '3':
194             //clearList(pHead);   //清空链表
195             printf("ListLength(L)=%d \n",ListLength(L));
196             printf("\n");
197             break;
198  
199         case '4':
200             L = SelectSort2(L);
201             //printf("%d \n", find);
202             ListTraverse(L);
203             printf("\n");
204             break;
205  
206         case '0':
207             exit(0);
208         }
209     }
210 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值