顺序表和链表

发布时间 2023-11-03 23:32:34作者: WCMS_XDU

writeup后面写

258.链表去重

给定一个键值为整数的单链表 L, 将键值的绝对值有重复的结点删除: 即对任意键值 K, 只有键值或其绝对值等于 K 的第一个结点被保留在 L 中。

例如, 下面单链表 L 包含键值 21-15157-15, 如下图(a) 所示; 去重后的链表 L 包含键值 21-157, 如下图(b)所示。

 

 

输入说明:

输入的第一行包含两个整数, 分别表示链表第一个结点的地址和结点个数 n1≤n≤100)。 结点地址是一个非负的 5 位整数, NULL 指针用-1 表示。

随后 n 行, 每行含 3 个整数, 按下列格式给出一个结点的信息: Address Key Next

其中 Address 是结点的地址, Key 是绝对值不超过 10000 的整数, Next 是下一个结点的地址。

 

输出说明:

输出的第一行为去重后链表 L 的长度, 换行; 接下来按顺序输出 L 的所有结点, 每个结点占一行, 按照 Address Key Next 的格式输出, 间隔 1 个空格。

 

测试样例:

输入样例 1

00100 5

99999 7 87654

23854 -15 00000

87654 -15 -1

00000 15 99999

00100 21 23854

 

输出样例 1

3 0 0100 21 23854

23854 -15 99999

99999 7 -1

 1 /*
 2   问题描述:
 3   给定一个键值为整数的单链表 L, 将键值的绝对值有重复的结点删除: 
 4   即对任意键值 K, 只有键值或其绝对值等于 K 的第一个结点被保留在 L 中。 
 5   例如, 下面单链表 L 包含键值 21、-15、 15、 7、 -15, 去重后的链表 L 包含键值 21、 -15、 7
 6   
 7   输入说明:
 8   输入的第一行包含两个整数, 分别表示链表第一个结点的地址和结点个数 n(1≤n≤100)。 
 9   结点地址是一个非负的 5 位整数, NULL 指针用-1 表示。
10   随后 n 行, 每行含 3 个整数, 按下列格式给出一个结点的信息:
11   Address Key Next
12   其中 Address 是结点的地址, Key 是绝对值不超过 10000 的整数, Next 是下一个结点的地址。
13   
14   输出说明:
15   输出的第一行为去重后链表 L 的长度, 换行; 接下来按顺序输出 L 的所有结点, 每个结点
16   占一行, 按照 Address Key Next 的格式输出, 间隔 1 个空格
17   
18   测试样例:
19   输入样例 1
20   00100 5
21   99999 7 87654
22   23854 -15 00000
23   87654 -15 -1
24   00000 15 99999
25   00100 21 23854
26   输出样例 1
27   3 
28   00100 21 23854
29   23854 -15 99999
30   99999 7 -1
31   
32     参考了CSDN后考虑数组模拟链表的方法,也就是静态链表法,其实课本上也有,没注意看到,,,;静态链表法同样有基本操作的实现;可以看到整个代码的实现是非常简洁的
33  */
34 #include<stdio.h>
35 #include<stdlib.h>
36 #include<math.h>
37 
38 struct
39 {
40     int key;
41     int next;
42 }Initarr[100000];//建立数组结构体,结构体内存键值和下一个结点的地址,而数组下标用来代替首地址
43 
44 int first_address,i,n,finalarr[100000],repeat[100000];//建立两个数组,一个是输出的final数组,一个是判断重复键值的repeat数组
45 
46 int main()
47 {
48     int len=0;
49     scanf("%d %d",&first_address,&n);
50     for(i=0;i<n;i++)
51     {
52         int a,b,c;
53         scanf("%d %d %d",&a,&b,&c);
54         Initarr[a].key=b;
55         Initarr[a].next=c;//将三个量存进去,这里用数组模拟链表,也就是数组实现静态链表,其实访问数组某元素也是访问他的内存头地址,类似的道理
56         
57     }
58     for(i=first_address;i>-1;i=Initarr[i].next)//for循环实现一个连续的遍历,从first_addrest到Initarr[].next存的地址(数组下标)最后到-1终止
59     {
60         int key=abs(Initarr[i].key);//取绝对值
61         if(repeat[key]==0)//这里用0,1标识是否重复,由于比较的是相同key(取绝对值了),所以repeat数组下标用key来指定唯一位置
62         {
63             repeat[key]=1;
64             finalarr[len++]=i;//不重复就把首地址存到final数组中,输出用
65         }
66         //不满足条件的话就会继续循环,当然也可以把剔除的存到另一个数组,用else分支写
67     }
68     printf("%d\n",len);
69     for(i=0;i<len;i++)
70     {
71         printf("%05d %d ",finalarr[i],Initarr[finalarr[i]].key);//先按格式输出首地址和键值;首地址按顺序存在final数组中,键值直接访问Init即可
72         if(i==len-1)
73             printf("-1\n");
74         else
75             printf("%05d\n",finalarr[i+1]);//显然next结点地址在i+1里
76         
77     }
78     
79     return 0;
80 }

 

264.反转链表

时间限制: 2 秒

内存限制: 256KB

问题描述

输入一个链表,反转链表后,输出链表的所有元素。

 

输入说明

输入第一行为整数n(n>=1),代表测试链表数。

从第二行开始每行表示一个链表,其中第一个数据表示链表中数据个数,其余数据表示要测试的链表中的数据,均为整数。 

输出说明

每一行对应一个链表反转后的元素。

 

输入样例

3

5 1 2 3 4 5

3 2 4 5

1 3

输出样例

5 4 3 2 1

5 4 2

3

两种写法  1 //想要反向输出只能写成双向链表?如果是单链表怎么操作?用数组也就是静态   2 //链表来模拟?  3 //

  4 // Created by Administrator on 2023/10/27/027.
  5 //
  6 
  7 /*试题名称    反转链表
  8 时间限制:    2 秒
  9 内存限制:    256KB
 10 
 11 问题描述
 12 输入一个链表,反转链表后,输出链表的所有元素。
 13 
 14 输入说明
 15 输入第一行为整数n(n>=1),代表测试链表数。
 16 从第二行开始每行表示一个链表,其中第一个数据表示链表中数据个数,其余数据表示要测试的链表中的数据,均为整数。
 17 
 18 输出说明
 19 每一行对应一个链表反转后的元素。
 20 
 21 输入样例
 22 3
 23 5 1 2 3 4 5
 24 3 2 4 5
 25 1 3
 26 
 27 输出样例
 28 5 4 3 2 1
 29 5 4 2
 30 3
 31 
 32 
 33 这个方法选取的是:
 34  直接用头插法建立单链表然后从头指针往后读取即可
 35  直接输出的话有个问题就是他会更新单链表,所以需要一个存储链表头指针的数组-》这个适用于你想要输入完全部再统一输出的情况,事实上直接输入一遍后输出一边oj不会管
 36  第二个问题是在oj里超时了
 37 
 38  提交成功后加了一些printf语句打印方便观看,用于oj可以注释掉
39 */ 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 44 typedef struct node 45 { 46 int data; 47 struct node *next; 48 }Linklist; 49 50 Linklist* headCreate(int len) 51 { 52 Linklist *head,*newData; 53 int my_data,i=0; 54 head=(Linklist*)malloc(sizeof(Linklist)); 55 head->next=NULL; 56 head->data=len; 57 for(i=0;i<len;i++) 58 { 59 newData=(Linklist*) malloc(sizeof(Linklist)); 60 scanf("%d",&my_data); 61 newData->data=my_data; 62 newData->next=head->next; 63 head->next=newData; 64 //要注意到头插法的特点是先进的在队尾,新的元素不断接在头结点的后面称为第一结点 65 } 66 return head; 67 } 68 69 void Visit_Linklist(Linklist *head) 70 { 71 Linklist *find; 72 find=head->next; 73 while(find!=NULL) 74 { 75 printf("%d\t",find->data); 76 find=find->next; 77 } 78 printf("\n"); 79 } 80 81 int main() 82 { 83 int sum,cnt,singleLen; 84 printf("请输入总的链表数:\n"); 85 scanf("%d",&sum); 86 87 Linklist ** lists = (Linklist **)malloc(sum * sizeof(Linklist *));//新建一个数组来存取每次的头指针,并用下标给定他唯一的标识符 88 //注意这个写法 89 printf("请输入每个链表的元素数和元素:\n"); 90 for(cnt=0;cnt<sum;cnt++) 91 { 92 scanf("%d",&singleLen); 93 lists[cnt]=headCreate(singleLen); 94 } 95 96 printf("反转后的链表是:\n"); 97 for(cnt=0;cnt<sum;cnt++) 98 { 99 Visit_Linklist(lists[cnt]); 100 } 101 102 //或者写成下面的输出也行: 103 /* 把list那个注释掉 104 * for(cnt=0;cnt<sum;cnt++) 105 { 106 scanf("%d",&singleLen); 107 Linklist *head=headCreate(singleLen); 108 Vist_Linklist(head); 109 } 110 */ 111 112 113 /* 114 1.在函数headCreate()中,使用了一个循环来读取输入数据并创建链表节点。 115 但是在每次循环时都调用getchar()来读取字符直到遇到换行符,这可能会导致程序在等待输入时耗费大量时间。(这个有可能是主要原因) 116 建议改为使用scanf函数读取整数,并在输入结束后清空缓冲区。 117 118 尝试改进后重新试下oj超时的问题 119 120 事实上验证了确实是这个getchar的问题,本来用的课本的例程代码,不过显然没考虑具体题目的时间限制情况 121 */ 122 123 return 0; 124 125 126 }

 

还有一种方法是尾插法然后用指针来转换。懒得把链表基本实现的函数写完了,,,

 

 1 //
 2 // Created by Administrator on 2023/10/27/027.
 3 //这个方法考虑的是尾插创建单链表再头插建新链表输出,实现倒序,可以看出尾插类似队列,头插类似栈
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 
 7 typedef struct node
 8 {
 9     int data;
10     struct node *next;
11 }Linklist;
12 
13 Linklist* reverseLinklist(Linklist *head)//反转链表,换为头插法
14 {
15     Linklist *new_head,*current;
16     new_head=NULL;
17     current=head;
18     while (current != NULL) {
19         Linklist * nextNode = current->next;//更新,将nextnode指针指向现指针指向位置的下一个结点(存储下一个结点的信息)
20         current->next = new_head;//将现指针的后继指针指向新链表的头指针,如一开始是指向NULL,下一次是指向上一个填入的结点
21         new_head = current;//更新头指针,新链表头指针指向现指针指向的结点,即赋值插入元素,由于上一步已经有current->next=newhead,实际完成了链接!
22         current = nextNode;//移动现指针到指向原链表的下一结点
23     }
24 
25     return new_head; // 返回新的头结点
26 }
27 
28 void Visit_Linklist(Linklist *head)
29 {
30     Linklist *find;
31     find=head;
32     while(find!=NULL)
33     {
34         printf("%d\t",find->data);
35         find=find->next;
36     }
37     printf("\n");
38 }
39 
40 int main()
41 {
42     int n; // 测试链表数
43     scanf("%d", &n);
44     int i,j;
45     for (i = 0; i < n; i++)
46     {
47         int len; // 链表长度
48         scanf("%d", &len);
49         Linklist *head,*tail;
50         head=NULL;
51         tail=NULL;
52         for (j = 0; j < len; j++)
53         {
54             int my_data;//尾插法存数据
55             Linklist *newData;
56             newData=(Linklist*) malloc(sizeof(Linklist));//给存储数据的新临时结点开辟空间
57             scanf("%d",&my_data);
58             newData->data=my_data;
59             if(head==NULL)//没有头结点,空表,头指针指向第一个填入的元素
60                 head=newData;
61             else
62                 tail->next=newData;//非空表,head不指向空,那么就讲tail后继指向新结点实现连接
63             tail=newData;//不管是空表的第一种情况,还是后续的非空表,都要有尾指针指向新临时结点
64         }
65         if(tail!=NULL)
66             tail->next=NULL;//尾插法单链表,最后尾指针后继指向空
67 
68         head=reverseLinklist(head);
69         Visit_Linklist(head);
70 
71         //可以在后面补一个释放内存的把链表内存释放了,养成好习惯
72     }
73 
74 }