一、数据准备
#define ERROR0
#define TRUE1
#define FALSE0
#define OK1
#define MAXSIZE20/* 存储空间初始分配量 */
typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
//定义结点
typedef struct Node{
ElemTypedata;
structNode*next;
}Node;
typedefstructNode* LinkList;
二、初始化单向链表
Status InitList(LinkList *L){
*L = (LinkList)malloc(sizeof(Node));
if(*L==NULL) {
returnERROR;
}
(*L)->next=NULL;
returnOK;
}
三、插入数据到单向链表
Status InsertList(LinkList *L,int index,ElemType data){
if(index<1) {
returnERROR;
}
if(*L==NULL) {
InitList(L);
}
LinkListp = *L;
inti=1;
while(i
p = p->next;
i++;
}
if(p ==NULL&& i==index) {
returnERROR;
}
//生成新结点
LinkList temp = (LinkList)malloc(sizeof(Node));
temp->data= data;
temp->next= p->next;
p->next= temp;
returnOK;
}
四、遍历单向链表
void TraverseList(LinkList L){
if(L ==NULL) {
return;
}
LinkListp = L->next;
while(p!=NULL) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
五、作业
1.题目:
将2个递增的有序链表合并为一个有序链表; 要求结果链表仍然使用两个链表的存储空间,不另外占用其他的存储空间. 表中不允许有重复的数据
void MergeList(LinkList *la,LinkList *lb,LinkList *lc){
//目标:将2个递增的有序链表La,Lb 合并为一个递增的有序链表Lc
if(*la==NULL&& lb ==NULL) {
return;
}else{
if(*la ==NULL) {
*lc = *lb;
}else{
*lc = *la;
}
}
//pa 是链表La的工作指针,pb 是链表Lb的工作指针, 初始化为首元结点;
*lc = *la;
LinkListpa = (*la)->next;
LinkListpb = (*lb)->next;
LinkListpc = (*lc);
LinkListtemp;
while(pa && pb) {
if(pa->data< pb->data) {
//取较小者La中的元素,将pa链接在pc的后面,pa指针后移
pc->next= pa;
pc = pa;
pa = pa->next;
}elseif(pa->data> pb->data){
//取较小者Lb的元素,将pb链接在pc后面, pb指针后移
pc->next= pb;
pc = pb;
pb = pb->next;
}else{
//相等时取La中的元素,删除Lb的元素;
pc->next= pa;
pc = pa;
pa = pa->next;
temp = pb->next;
free(pb);
pb = temp;
}
}
//将非空表的剩余元素之间链接在Lc表的最后
pc->next= pa?pa:pb;
//释放Lb的头结点
free(*lb);
}
2.作业2:
题目:
已知两个链表A和B分别表示两个集合.其元素递增排列. 设计一个算法,用于求出A与B的交集,并存储在A链表中;
例如:
La = {2,4,6,8}; Lb = {4,6,8,10};
Lc = {4,6,8}
void Intersection(LinkList *la,LinkList *lb,LinkList *lc){
//目标: 求2个递增的有序链表La,Lb的交集, 使用头指针Lc指向带回;
*lc = *la;
LinkListpa,pb,pc,temp;
//用pa,pb指向la,lb中的数据,进行比较
pa = (*la)->next;
pb = (*lb)->next;
pc = *lc;
while(pa && pb) {
if(pa->data== pb->data) {
//相等取pa,删除pb
pc->next= pa;
pc = pa;
pa = pa->next;
temp = pb->next;
free(pb);
pb = temp;
}elseif(pa->data> pb->data){
//删除较小的pb
temp = pb;
pb = pb->next;
free(temp);
}else{
//删除较小的pa
temp = pa->next;
free(pa);
pa = temp;
}
}
//删除后面没比较过的数据
while(pa) {
temp = pa;
pa = pa->next;
free(temp);
}
while(pb) {
temp = pb;
pb = pb->next;
free(temp);
}
//
pc->next=NULL;
free(*lb);
}
3.作业3:
题目:
设计一个算法,将链表中所有节点的链接方向"原地旋转",即要求仅仅利用原表的存储空间. 换句话说,要求算法空间复杂度为O(1);
例如:L={0,2,4,6,8,10}, 逆转后: L = {10,8,6,4,2,0};
//后插法
void Inverse(LinkList *L){
LinkListp = (*L)->next;
(*L)->next=NULL;
LinkList q;
while(p !=NULL) {
q = p->next;
p->next= (*L)->next;
(*L)->next= p;
p = q;
}
}
4.作业4:
题目:
设计一个算法,删除递增有序链表中值大于等于mink且小于等于maxk(mink,maxk是给定的两个参数,其值可以和表中的元素相同,也可以不同)的所有元素;
解法一:
voidDeleteMinMax(LinkList*L,intmink,intmaxk){
LinkListp = (*L)->next;
LinkListq = (*L);
LinkListtemp;
while(p) {
if(p->data>mink && p->data< maxk) {
temp = p;
p = p->next;
q->next= p;
free(temp);
}else{
q = p;
p = p->next;
}
}
}
解法二:
//解法二:
voidDeleteMinMax2(LinkList*L,intmink,intmaxk){
//目标: 删除递增有序链表L中值大于等于mink 和小于等于maxk的所有元素
LinkListp,q,pre;
pre = *L;
LinkListtemp;
//p指向首元结点
p = (*L)->next;
//1.查找第一值大于mink的结点
while(p && p->data< mink) {
//指向前驱结点
pre = p;
p = p->next;
}
//2.查找第一个值大于等于maxk的结点
while(p && p->data<=maxk) {
p = p->next;
}
//3.修改待删除的结点指针
q = pre->next;
pre->next= p;
while(q != p) {
temp = q->next;
free(q);
q = temp;
}
}
5.题目5:
设将n(n>1)个整数存放到一维数组R中, 试设计一个在时间和空间两方面都尽可能高效的算法;将R中保存的序列循环左移p个位置(0
例如: pre[10] = {0,1,2,3,4,5,6,7,8,9},
n = 10,p = 3;
pre[10] = {3,4,5,6,7,8,9,0,1,2}
//交换元素
voidReverse(int*pre,intleft,intright){
inti = left;
intj = right;
while(i
pre[i] = pre[i] + pre[j];
pre[j] = pre[i] - pre[j];
pre[i] = pre[i] - pre[j];
i++;
j--;
}
}
//n为数组长度,p为移动几位,p<n
voidLeftShift(int*pre,intn,intp){
//将长度为n的数组pre 中的数据循环左移p个位置
if(p>0&& p
//1. 将数组中所有元素全部逆置
Reverse(pre,0, n-1);
//2. 将前n-p个数据逆置
Reverse(pre,0, n-1-p);
//3. 将后p个数据逆置
Reverse(pre, n-p, n-1);
}
}
6.题目6:
已知一个整数序列A = (a0,a1,a2,...an-1),其中(0<= ai <=n),(0<= i<=n). 若存在ap1= ap2 = ...= apm = x,且m>n/2(0<=pk<n,1<=k<=m),则称x 为 A的主元素. 例如,A = (0,5,5,3,5,7,5,5),则5是主元素; 若B = (0,5,5,3,5,1,5,7),则A中没有主元素,假设A中的n个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出数组元素中的主元素,若存在主元素则输出该元素,否则输出-1.
int MainElement(int *A,int n){
//目标: 求整数序列A中的主元素;
//count 用来计数
intcount =1;
//key 用来保存候选主元素, 初始A[0]
intkey = A[0];
//1,3,5,2,5,5,5
//(1) 扫描数组,选取候选主元素
for(inti=1; i
if(A[i] == key) {
//(2) 如果A[i]元素值 == key ,则候选主元素计数加1;
count++;
}else{
if(count>0) {
//(3) 当前元素A[i] 非候选主元素,计数减1;
count--;
}else{
//(4) 如果count 等于0,则更换候选主元素,重新计数
key = A[i];
count =1;
}
}
}
//如果count >0
if(count>0) {
//(5)统计候选主元素的实际出现次数
for(inti=0; i
if(A[i] == key) {
count++;
}
}
}
//(6)判断count>n/2, 确认key是不是主元素
if(count>n/2) {
returnkey;
}
//不存在主元素
return-1;
}
7.题目7:
用单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计一个时间复杂度尽可能高效的算法. 对于链表中的data 绝对值相等的结点, 仅保留第一次出现的结点,而删除其余绝对值相等的结点.例如,链表A = {21,-15,15,-7,15}, 删除后的链表A={21,-15,-7};
void DeleteEqualNode(LinkList *L,int n){
//目标: 删除单链表中绝对值相等的结点;
//1. 开辟辅助数组p.
int*p =alloca(sizeof(int)*n);
// int *p = malloc(sizeof(int)*n);
//2.数组元素初始值置空
for(inti=0; i
*(p+i) =0;
}
//3.指针temp 指向首元结点
//4.遍历链表,直到temp = NULL;
LinkListtemp = (*L)->next;
LinkListr = *L;
while(temp !=NULL) {
//5.如果该绝对值已经在结点上出现过,则删除该结点
if(p[abs(temp->data)] ==1) {
//移除
//临时指针指向temp->next
r->next= temp->next;
//删除temp指向的结点
free(temp);
//temp 指向删除结点下一个结点
temp = r->next;
}else{
//6. 未出现过的结点,则将数组中对应位置置为1;
p[abs(temp->data)] =1;//记录已经出现过了
r = temp;
//继续向后遍历结点
temp = temp->next;
}
}
}
六、验证
intmain(intargc,constchar* argv[]) {
printf("线性表练习篇!\n");
//链表相关的题目,有兴趣的可以尝试:https://leetcode-cn.com/tag/linked-list/
StatusiStatus;
LinkListLa,Lb,Lc,L;
InitList(&La);
InitList(&Lb);
//// ---------作业1--------
// printf("******题目1:********\n");
// //设计2个递增链表La,Lb
// for(int j = 10;j>=0;j-=2)
// {
// iStatus = InsertList(&La, 1, j);
// }
// printf("La:\n");
// TraverseList(La);
//
// for(int j = 11;j>0;j-=2)
// {
// iStatus = InsertList(&Lb, 1, j);
// }
// printf("Lb:\n");
// TraverseList(Lb);
//
// MergeList(&La, &Lb, &Lc);
// printf("Lc:\n");
// TraverseList(Lc);
// /*---------作业2--------*/
// printf("******题目2:********\n");
// InsertList(&La, 1, 8);
// InsertList(&La, 1, 6);
// InsertList(&La, 1, 4);
// InsertList(&La, 1, 2);
// printf("La:\n");
// TraverseList(La);
//
//
// InsertList(&Lb, 1, 10);
// InsertList(&Lb, 1, 8);
// InsertList(&Lb, 1, 6);
// InsertList(&Lb, 1, 4);
// printf("Lb:\n");
// TraverseList(Lb);
//
// Intersection(&La, &Lb, &Lc);
// printf("Lc:\n");
// TraverseList(Lc);
// /*---------作业3--------*/
// printf("******题目3:********\n");
// InitList(&L);
// for(int j = 10;j>=0;j-=2)
// {
// iStatus = InsertList(&L, 1, j);
// }
// printf("L逆转前:\n");
// TraverseList(L);
//
// Inverse(&L);
// printf("L逆转后:\n");
// TraverseList(L);
// /*---------作业4--------*/
// printf("******题目4:********\n");
// InitList(&L);
// for(int j = 10;j>=0;j-=2)
// {
// iStatus = InsertList(&L, 1, j);
// }
// printf("L链表:\n");
// TraverseList(L);
//
// DeleteMinMax(&L, 4, 10);
// printf("删除链表mink与maxk之间结点的链表:\n");
// TraverseList(L);
// /*---------作业5--------*/
// printf("******题目5:********\n");
// int pre[10] = {0,1,2,3,4,5,6,7,8,9};
// LeftShift(pre, 10, 3);
// for (int i=0; i < 10; i++) {
// printf("%d ",pre[i]);
// }
// printf("\n");
// // /*---------作业6--------*/
// printf("******题目6:********\n");
// int A[] = {0,5,5,3,5,7,5,5};
// int B[] = {0,5,5,3,5,1,5,7};
// int C[] = {0,1,2,3,4,5,6,7};
//
// int value = MainElement(A, 8);
// printf("数组A 主元素为: %d\n",value);
// value = MainElement(B, 8);
// printf("数组B 主元素为(-1表示数组没有主元素): %d\n",value);
// value = MainElement(C, 8);
// printf("数组C 主元素为(-1表示数组没有主元素): %d\n",value);
/*---------作业7--------*/
//21,-15,15,-7,15
printf("******题目7:********\n");
InitList(&L);
InsertList(&L,1,21);
InsertList(&L,1, -15);
InsertList(&L,1,15);
InsertList(&L,1, -7);
InsertList(&L,1,15);
DeleteEqualNode(&L,21);
TraverseList(L);
return0;
}