本文使用的链表创建代码如下:
#include<stdio.h>
#include<stdlib.h>
#define N 100
typedef int ElementType;
typedef struct Lnode{
ElementType data;
struct Lnode *next;
}LNode, *Linklist;
Linklist Create_list_tail(Linklist head){
//尾插法(含头结点)
head = (Linklist)malloc(sizeof(LNode));
Linklist node = NULL;
Linklist end = NULL;
head->next = NULL; //若没有后续结点,则将next值为null
end = head; //若没有后续结点,则记录尾结点为头结点。
int count = 0; //结点个数
scanf("%d", &count);
for (int i = 0; i < count; i++){
node = (LNode *)malloc(sizeof(LNode)); //为新结点开辟空间
node->data = i * 0; //为新的结点数据域赋值
end->next = node; // 将新结点的值给上一个结点的next
end = node; //将新结点标记为尾结点
}
end->next = NULL; //将尾结点的next值赋为NULL
return head;
}
1 两个链表的合并
1.1 升序排序
题目描述:
给定键值按升序排列的带头结点的单链表La和Lb,将其合并成升序排列的单链表,并返回新链表的表头指针。要求利用原表的结点数据空间。
实现代码:
LNode * Merge_linklist(Linklist La, Linklist Lb){
//对两个含有头结点的升序链表进行合并,合并后仍然为升序。
LNode *Lc, *pa, *pb, *pc, *ptemp;
//选择其中一个链表作为首节点,进行初始化,如选择La
Lc = La;
pc = La;
pa = La->next;
pb = Lb->next;
while(pa!= NULL && pb!=NULL){
if (pa->data < pb->data){
pc->next = pa;
pc = pa;
pa = pa->next;
}else if (pa->data > pb->data){
pc->next = pb;
pc = pb;
pb = pb->next;
}else{
//若两值相等,则取其中一个,并释放另外一个空间
pc->next = pa;
pc = pa;
ptemp = pb;
pa = pa->next;
pb = pb->next;
free(ptemp);
}
}
// 将未到末尾的链表拼接到新链表后面。
if(pa != NULL) pc->next = pa;
else pc->next = pb;
//释放Lb的头结点
free(Lb);
return Lc;
}
1.2 降序排序
题目描述:
对两个带头结点的升序链表la和lb进行合并,合并后的链表依旧为升序,要求利用原表的数据空间
LNode * Merge_linklist_reverse(Linklist La, Linklist Lb){
//对两个带头结点的升序链表la和lb进行合并,合并后的链表依旧为升序,要求利用原表的数据空间
LNode *Lc,*pa,*pb,*pc,*ptemp;
pa = La->next;
pb = Lb->next;
Lc = La;
Lc->next = NULL;
while(pa != NULL && pb != NULL){
if(pa->data < pb->data){
pc = pa;
//先将pa下一个结点找出来,再修改pc,也就是pa
pa = pa->next;
pc->next = Lc->next;
Lc->next = pc;
}else if (pa->data > pb->data){
pc = pb;
pb = pb->next;
pc->next = Lc->next;
Lc->next = pc;
}else{
pc = pa;
ptemp = pb;
pa = pa->next;
pb = pb->next;
free(ptemp);
pc->next = Lc->next;
Lc->next = pc;
}
}
while(pa != NULL){
pc = pa;
pa = pa->next;
pc->next = Lc->next;
Lc->next = pc;
}
while(pb != NULL){
pc = pb;
pb = pb->next;
pc->next = Lc->next;
Lc->next = pc;
}
free(Lb);
return Lc;
}
2.对链表进行去重
对链表中的元素进行去重,要求在原数据空间中操作。
链表中元素打印代码:
void print_list(Linklist head){
//打印带头结点链表的所有元素
LNode *p;
p = head->next;
while(p != NULL){
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
2.1 方法1—双循环法
算法实现思路:
利用双循环法进行实现。若链表中首个元素为1,则将后续元素一次与1进行相比,若相同则进行删除,否则进行下一个进行对比;获取链表中第二个元素,若为2,则将后续元素依次与2对比,相同则删除,否则继续。
算法的时间复杂度O(n²),空间复杂度为O(1)
Linklist ElementType_unique(Linklist L){
//对带头结点的链表L进行去重,对原表去重后,并返回该链表;
//算法时间复杂度为O(N**2),空间复杂度为O(n);
LNode *p, *compr_pre, *compr, *temp;
p = L->next;
while (p != NULL){
compr = p->next;
compr_pre = p;
while(compr != NULL){
if(p->data == compr->data){
compr_pre -> next = compr->next;
//compr_pre = compr;
temp = compr;
compr = compr->next;
free(temp);
}else{
compr_pre = compr;
compr = compr->next;
}
}
p = p->next;
}
}
2.1 方法2—哈希法
算法实现思路:
该方法仅仅针对数组中元素为整数的情况,利用哈希思想进行实现。假设链表中的最大值为N-1,创建一个长度为N的数组,并将所有元素初始化为0;循环获取链表的值,并写入数组对应的值中,若值大于1,则说明该元素重复,需要进行删除,等于1说明该元素有出现,等于0说明该元素未曾出现。
算法的时间复杂度O(n),空间复杂度为O(N)
实现代码如下:
Linklist Elem_unique_hash(Linklist L){
//对带头结点的链表L进行去重(链表元素需要均为整数,且不大于N)
int arr[N]={
0};
LNode *p, *m, *temp;
p = L->next;
m = L;
while(p != NULL){
//对数字的值进行计数
arr[p->data]++;
if (arr[p->data] > 1){
//对计数大于1的元素进行删除
m->next = p->next;
temp = p;
p = p->next;
free(temp);
}else{
m = m->next;
p = p->next;
}
}
return L;
}
3.顺序表变逆序
将顺序排列带头结点链表L进行逆序排列,要求使用原表空间
LNode * reverse_list(Linklist L){
//将顺序排列带头结点链表L进行逆序排列,要求使用原表空间
LNode * p, * temp;
p = L->next;
L->next = NULL;
temp = NULL;
while(p!=NULL){
/*写法1:注意先后顺序,否则容易造成链表断裂
temp = p;
p = p->next;
temp->next = L->next;
L->next = temp;*/
//写法2
temp = p->next;
p->next = L->next;
L->next = p;
p = temp;
}
return L;
}
4.其他
如果对线性表的操作有两种,即删除第一个元素,在最后一个元素的后面插入新元素,那么最好使用:
A. 只有表头指针没有表尾指针的循环单链表;
B.只有表头指针没有表尾指针的循环单链表;
C.非循环单链表;
D.循环单链表;
答案:B
选项 | 删除第一个元素的时间复杂度 | 插入新元素 |
---|---|---|
A | O(1) | O(n) |
B | O(1) | O(1) |
C | O(1) | O(n) |
D | O(1) | O(1) |