单链表排序是单链表的常见编程任务之一,也是面试中经常出现的题目。单链表排序的关键是交换算法,需要额外考虑。选择排序是比较直观的排序算法之一,这里就使用选择排序实现单链表的排序。
如果需要对选择排序复习一下,传送门:算法导论:选择排序的原理与实现。
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 | } |