PTA鏈表習題集合[11-2-1~11-2-9]
文章目錄
1.實驗11-2-1 建立學生信息鏈表 (20分)
本題要求實現一個將輸入的學生成績組織成單向鏈表的簡單函數。
函數接口定義:(尾插法)
void input();
該函數利用scanf
從輸入中獲取學生的信息,並將其組織成單向鏈表。鏈表節點結構定義如下:
struct stud_node {
int num; /*學號*/
char name[20]; /*姓名*/
int score; /*成績*/
struct stud_node *next; /*指向下個結點的指針*/
};
單向鏈表的頭尾指針保存在全局變量head
和tail
中。
輸入爲若干個學生的信息(學號、姓名、成績),當輸入學號爲0時結束。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stud_node {
int num;
char name[20];
int score;
struct stud_node *next;
};
struct stud_node *head, *tail;
void input();
int main()
{
struct stud_node *p;
head = tail = NULL;
input();
for ( p = head; p != NULL; p = p->next )
printf("%d %s %d\n", p->num, p->name, p->score);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 zhang 78
2 wang 80
3 li 75
4 zhao 85
0
輸出樣例:
1 zhang 78
2 wang 80
3 li 75
4 zhao 85
函數代碼:
void input()
{
//已存在head,tail;
int num;
char name[20];
int score;
struct stud_node *p;
//輸入學號姓名班級
scanf("%d",&num);
//學號不爲0時循環
while(num!=0)
{
scanf("%s%d",name,&score);
//申請新結點
p = (struct stud_node*)malloc(sizeof(struct stud_node));
p->next=NULL; //新結點的下一個結點指空
//賦值新結點
p->num=num;
strcpy(p->name,name);
p->score=score;
//判斷是否爲首結點,是則直接賦值,不是則尾接
if(tail==NULL)
head=p;
else
{
tail->next=p;
}
tail=p; //尾指針移動
scanf("%d",&num);
}
}
2.實驗11-2-2學生成績鏈表處理(20分)
本題要求實現兩個函數,一個將輸入的學生成績組織成單向鏈表;另一個將成績低於某分數線的學生結點從鏈表中刪除。
函數接口定義:
struct stud_node *createlist();
struct stud_node *deletelist( struct stud_node *head, int min_score );
函數createlist
利用scanf
從輸入中獲取學生的信息,將其組織成單向鏈表,並返回鏈表頭指針。鏈表節點結構定義如下:
struct stud_node {
int num; /*學號*/
char name[20]; /*姓名*/
int score; /*成績*/
struct stud_node *next; /*指向下個結點的指針*/
};
輸入爲若干個學生的信息(學號、姓名、成績),當輸入學號爲0時結束。
函數deletelist
從以head
爲頭指針的鏈表中刪除成績低於min_score
的學生,並返回結果鏈表的頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct stud_node {
int num;
char name[20];
int score;
struct stud_node *next;
};
struct stud_node *createlist();
struct stud_node *deletelist( struct stud_node *head, int min_score );
int main()
{
int min_score;
struct stud_node *p, *head = NULL;
head = createlist();
scanf("%d", &min_score);
head = deletelist(head, min_score);
for ( p = head; p != NULL; p = p->next )
printf("%d %s %d\n", p->num, p->name, p->score);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 zhang 78
2 wang 80
3 li 75
4 zhao 85
0
80
輸出樣例:
2 wang 80
4 zhao 85
函數代碼:
struct stud_node *createlist()
{
//已存在head,tail;
int num;
char name[20];
int score;
struct stud_node *p,*head=NULL,*tail;
tail=head;
//輸入學號
scanf("%d",&num);
//學號不爲0時循環
while(num!=0)
{
//申請新結點
p = (struct stud_node*)malloc(sizeof(struct stud_node));
//賦值新結點
scanf("%s %d",p->name,&p->score);
p->num=num;
p->next=NULL; //新結點的下一個結點指空
//判斷是否爲首結點,是則直接賦值,不是則尾接
if(tail==NULL)
head=p;
else
{
tail->next=p;
}
tail=p; //尾指針移動
scanf("%d",&num);
}
return head;
}
struct stud_node *deletelist( struct stud_node *head, int min_score )
{
//刪除成績低於min_score的學生,返回頭指針
struct stud_node *move,*front;
move=head;
front=NULL;
while(move)
{
//若符合小於min_score
if(move->score<min_score)
{
//若爲第一個,則移動頭指針後移一位
if(front==NULL)
head=head->next;
//若非第一位則,move的前一個節點指向move的後一個節點,跳過move
else
front->next=move->next;
}//不符合則front移動到當前move的位置 保留前一個結點做標記
else
front=move;
//move向下一個結點移動
move=move->next;
}
return head;
}
3.實驗11-2-3逆序數據建立鏈表 (20分)
本題要求實現一個函數,按輸入數據的逆序建立一個鏈表。
函數接口定義:(頭插法)
struct ListNode *createlist();
函數createlist
利用scanf
從輸入中獲取一系列正整數,當讀到−1時表示輸入結束。按輸入數據的逆序建立一個鏈表,並返回鏈表頭指針。鏈表節點結構定義如下:
struct ListNode {
int data;
struct ListNode *next;
};
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist();
int main()
{
struct ListNode *p, *head = NULL;
head = createlist();
for ( p = head; p != NULL; p = p->next )
printf("%d ", p->data);
printf("\n");
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 2 3 4 5 6 7 -1
輸出樣例:
7 6 5 4 3 2 1
函數代碼:
struct ListNode *createlist()
{
int data;
struct ListNode *head = (struct ListNode*)malloc(sizeof(struct ListNode)); //創建一個空的頭結點
struct ListNode *insert;
head->next=NULL; //頭指針指空
insert=head; //建立一個指針指向頭結點
scanf("%d",&data);
while(data!=-1)
{
struct ListNode *p = (struct ListNode*)malloc(sizeof(struct ListNode)); //申請新結點
p->data=data;
//頭插法核心
p->next=insert->next; //新節點指向頭指針的下一個節點
insert->next=p; //頭指針指向新節點
scanf("%d",&data);
}
return head->next; //返回的不是頭指針而是首個結點,既頭指針指向的下一個結點
}
4.實驗11-2-4 刪除單鏈表偶數節點(20分)
本題要求實現兩個函數,分別將讀入的數據存儲爲單鏈表、將鏈表中偶數值的結點刪除。鏈表結點定義如下:
struct ListNode {
int data;
struct ListNode *next;
};
函數接口定義:
struct ListNode *createlist();
struct ListNode *deleteeven( struct ListNode *head );
函數createlist
從標準輸入讀入一系列正整數,按照讀入順序建立單鏈表。當讀到−1時表示輸入結束,函數應返回指向單鏈表頭結點的指針。
函數deleteeven
將單鏈表head
中偶數值的結點刪除,返回結果鏈表的頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist();
struct ListNode *deleteeven( struct ListNode *head );
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *head;
head = createlist();
head = deleteeven(head);
printlist(head);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 2 2 3 4 5 6 7 -1
輸出樣例:
1 3 5 7
函數代碼:
/* 你的代碼將被嵌在這裏 */
struct ListNode *createlist()
{
struct ListNode *head=NULL,*p,*last;
int n;
last=head; //last指針用於移動表示鏈表末尾
do{
scanf("%d",&n); //輸入結點數值
if(n!=-1)
{
p=(struct ListNode *)malloc(sizeof(struct ListNode)); //申請新節點
p->data=n; //新節點賦值
p->next=NULL; //新節點指空
if(last==NULL) //若爲頭結點時
head=p;
else
last->next=p; //非頭結點時,尾接
last=p; //標誌位移動到新節點處
}
else
break;
}while(n!=-1);
return head;
}
struct ListNode *deleteeven( struct ListNode *head )
{
//刪除偶數結點
struct ListNode *move,*front;
move=head;
front=NULL;
while(move)
{
//若符合data爲偶數
if((move->data)%2==0)
{
//若爲第一個,則移動頭指針後移一位
if(front==NULL)
head=head->next;
//若非第一位則,move的前一個節點指向move的後一個節點,跳過move
else
front->next=move->next;
}//不符合則front移動到當前move的位置 保留前一個結點做標記
else
front=move;
//move向下一個結點移動
move=move->next;
}
return head;
}
5.實驗11-2-5鏈表拼接(20分)
本題要求實現一個合併兩個有序鏈表的簡單函數。鏈表結點定義如下:
struct ListNode {
int data;
struct ListNode *next;
};
函數接口定義:
struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2);
其中list1
和list2
是用戶傳入的兩個按data
升序鏈接的鏈表的頭指針;函數mergelists
將兩個鏈表合併成一個按data
升序鏈接的鏈表,並返回結果鏈表的頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判實現,細節不表*/
struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2);
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *list1, *list2;
list1 = createlist();
list2 = createlist();
list1 = mergelists(list1, list2);
printlist(list1);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 3 5 7 -1
2 4 6 -1
輸出樣例:
1 2 3 4 5 6 7
函數代碼:
/* 你的代碼將被嵌在這裏 */
struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2)
{
//函數mergelists將兩個鏈表合併成一個按data升序鏈接的鏈表 --合併---升序
//思路1.是相當於刪除後一個鏈表的結點,在插入到前一個鏈表裏。
//思路2,先合併兩個鏈表,在利用指針變量做若干次冒泡排序。
if(list1==NULL&&list2)
return list2;
if(list2==NULL&&list1)
return list1;
if(list1==NULL&&list2==NULL)
return NULL;
struct ListNode *move1,*move2,*save,*front; //move1.2 分別用於移動,save用來指向從list2取下結點的地址
move2=list2;
move1=list1;
while(move2)
{
//<---取結點--->
save=move2; //保存list2當前節點
move2=move2->next; //list2首結點後移
save->next=NULL; //當前節點指空 ,取下結點
//<---插入結點到符合條件位置--->
//重置move1首結點地址,front指針
move1=list1;
front=NULL;
while(move1)
{
if(save->data<move1->data)
{
if(front)
{
//頭插法核心 ,當data的值在list1中間時
save->next=front->next;
front->next=save;
break;
}else{
//當data的值比list1所有值都小的時候
save->next=list1;
list1=save;
break;
}
}else
front=move1;
move1=move1->next;
}
//尾插法 當data的值比list1所有值都大的時候
if(move1==NULL)
{
front->next=save;
}
}
//返回list1首地址
return list1;
}
6.實驗11-2-6 奇數值結點鏈表 (20分)
本題要求實現兩個函數,分別將讀入的數據存儲爲單鏈表、將鏈表中奇數值的結點重新組成一個新的鏈表。鏈表結點定義如下:
struct ListNode {
int data;
ListNode *next;
};
函數接口定義:
struct ListNode *readlist();
struct ListNode *getodd( struct ListNode **L );
函數readlist
從標準輸入讀入一系列正整數,按照讀入順序建立單鏈表。當讀到−1時表示輸入結束,函數應返回指向單鏈表頭結點的指針。
函數getodd
將單鏈表L
中奇數值的結點分離出來,重新組成一個新的鏈表。返回指向新鏈表頭結點的指針,同時將L
中存儲的地址改爲刪除了奇數值結點後的鏈表的頭結點地址(所以要傳入L
的指針)。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *readlist();
struct ListNode *getodd( struct ListNode **L );
void printlist( struct ListNode *L )
{
struct ListNode *p = L;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *L, *Odd;
L = readlist();
Odd = getodd(&L);
printlist(Odd);
printlist(L);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 2 2 3 4 5 6 7 -1
輸出樣例:
1 3 5 7
2 2 4 6
函數代碼:
/* 你的代碼將被嵌在這裏 */
struct ListNode *readlist()
{
struct ListNode *head=NULL,*p,*last;
int n;
last=head; //last指針用於移動表示鏈表末尾
do{
scanf("%d",&n); //輸入結點數值
if(n!=-1)
{
p=(struct ListNode *)malloc(sizeof(struct ListNode)); //申請新節點
p->data=n; //新節點賦值
p->next=NULL; //新節點指空
if(last==NULL) //若爲頭結點時
head=p;
else
last->next=p; //非頭結點時,尾接
last=p; //標誌位移動到新節點處
}
else
break;
}while(n!=-1);
return head;
}
struct ListNode *getodd( struct ListNode **L )
{
//奇數分離構成新鏈表,返回刪除奇數的鏈表的頭結點
struct ListNode *odd=NULL,*last,*p,*q,*k;
last=odd; //用於奇數鏈表鏈表 插入新節點
p=*L; //表示原鏈表移動指針
q=NULL; //q初始化,作爲原鏈表移動指針前一個節點的標記
while(p)
{
if((p->data)%2) //data取餘2爲 1 則爲奇數,刪除節點
{
if(q&&p!=*L) //非L收個節點就是奇數的情況
{
k=p; //保存奇數節點
q->next=p->next; //刪除奇數節點
if(last!=NULL) //插入odd鏈表
last->next=k;
else
odd=k;
last=k;
}
else //L的第一個就是奇數的情況
{
k=p; //保存奇數節點
*L=p->next; //頭結點向後移動,刪除奇數節點
if(last!=NULL) //插入odd鏈表
last->next=k;
else
odd=k;
last=k;
}
}
else
q=p; //未找到奇數節點,則用q保留當前位置
p=p->next; //p指針繼續向下移動
if(last) //odd鏈表非空則
last->next=NULL; //odd末尾指針指空
}
return odd;
}
7.實驗11-2-7 統計專業人數 (15分)
本題要求實現一個函數,統計學生學號鏈表中專業爲計算機的學生人數。鏈表結點定義如下:
struct ListNode {
char code[8];
struct ListNode *next;
};
這裏學生的學號共7位數字,其中第2、3位是專業編號。計算機專業的編號爲02。
函數接口定義:
int countcs( struct ListNode *head );
其中head
是用戶傳入的學生學號鏈表的頭指針;函數countcs
統計並返回head
鏈表中專業爲計算機的學生人數。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct ListNode {
char code[8];
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判實現,細節不表*/
int countcs( struct ListNode *head );
int main()
{
struct ListNode *head;
head = createlist();
printf("%d\n", countcs(head));
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1021202
2022310
8102134
1030912
3110203
4021205
#
輸出樣例:
3
函數代碼:
int countcs( struct ListNode *head )
{
//統計並返回head鏈表中專業爲計算機的學生人數
int i,count=0,item;
struct ListNode *p=head;
//p指針用來遍歷鏈表
while(p)
{
//只讀取code數組下標爲1.2的字符
for(item=0,i=1;i<=2;i++)
{
//字符轉整數
item=item*10+(p->code[i]-'0');
}
//判斷是否爲2
if(item==2)
{
count++;
}
//移動指針
p=p->next;
}
//返回計數
return count;
}
8.實驗11-2-8 單鏈表結點刪除 (20分)
本題要求實現兩個函數,分別將讀入的數據存儲爲單鏈表、將鏈表中所有存儲了某給定值的結點刪除。鏈表結點定義如下:
struct ListNode {
int data;
ListNode *next;
};
函數接口定義:
struct ListNode *readlist();
struct ListNode *deletem( struct ListNode *L, int m );
函數readlist
從標準輸入讀入一系列正整數,按照讀入順序建立單鏈表。當讀到−1時表示輸入結束,函數應返回指向單鏈表頭結點的指針。
函數deletem
將單鏈表L
中所有存儲了m
的結點刪除。返回指向結果鏈表頭結點的指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *readlist();
struct ListNode *deletem( struct ListNode *L, int m );
void printlist( struct ListNode *L )
{
struct ListNode *p = L;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
int m;
struct ListNode *L = readlist();
scanf("%d", &m);
L = deletem(L, m);
printlist(L);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
10 11 10 12 10 -1
10
輸出樣例:
11 12
函數代碼:
struct ListNode *readlist()
{
//---**尾插法**---
//定義3個指針變量
//head用於返回鏈表的首結點,先初始化指空
//p 用於申請新節點
//last 用於連接head和新節點的一個尾指針,並且每次都停留在鏈表結尾
struct ListNode *head=NULL,*p,*last;
int n;
//先把last指向head ,起始時首尾都在一起
last=head;
do{
//輸入節點信息
scanf("%d",&n);
//輸入值!= -1時 操作
if(n!=-1)
{
//---申請新節點---
p=(struct ListNode *)malloc(sizeof(struct ListNode));
p->data=n;
p->next=NULL; //申請的新節點初始化,下一個節點指空
//---連接節點----
//head需要返回,不能用來移動,當檢測到尾指針指空,表示首尾指針並未有任何值
if(last==NULL)
head=p; //直接把第一個節點賦值到head
else
last->next=p; //若last非空 則把p連接到last後一個節點 head->last->p->null;
last=p; //連接完後last移動到到p上,既移動到新的結尾 head->last->p1->last(p)->null;
}
else
break;
}while(n!=-1);// -1結束循環
return head;
}
struct ListNode *deletem( struct ListNode *L,int m )
{
//--由於單鏈表遍歷是不可逆的---
//設置兩個指針一個控制移動,一個用於連接(防止斷鏈)
struct ListNode *p,*q;
p=L; //p用於移動
q=NULL; //q初始化
//當p非空時
while(p)
{
//若data爲m
if(p->data==m)
{
//情況1:中間節點或者末尾節點是m ,此時 q!=NULL
if(q)
q->next=p->next; //q->m(p)->x q->x
//情況2:首節點就是m ,此時 q=NULL
else
L=p->next; //則直接移動鏈表的首結點後移
}
else//data不爲m,則q移動到p當前位置,相當於記錄p前一個節點
q=p;
//p指針移動到下一個節點
p=p->next;
}
return L;
}
9.實驗11-2-9 鏈表逆置 (20分)
本題要求實現一個函數,將給定單向鏈表逆置,即表頭置爲表尾,表尾置爲表頭。鏈表結點定義如下:
struct ListNode {
int data;
struct ListNode *next;
};
函數接口定義:
struct ListNode *reverse( struct ListNode *head );
其中head
是用戶傳入的鏈表的頭指針;函數reverse
將鏈表head
逆置,並返回結果鏈表的頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判實現,細節不表*/
struct ListNode *reverse( struct ListNode *head );
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *head;
head = createlist();
head = reverse(head);
printlist(head);
return 0;
}
/* 你的代碼將被嵌在這裏 */
輸入樣例:
1 2 3 4 5 6 -1
輸出樣例:
6 5 4 3 2 1
函數代碼:
struct ListNode *reverse( struct ListNode *head )
{
//時間複雜度爲O(n),空間複雜度爲O(1)
//特殊情況,空鏈表
if(head==NULL)
return NULL;
//申請一個空的結點作爲頭結點
struct ListNode *fake = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *front,*now,*tail;
fake->next=head;
front=head;
//當now==NULL時,結束循環
do{
//指針移動
now=front->next;
if(now==NULL)
break;
tail=now->next;
//頭插法
front->next=tail;
now->next=fake->next;
fake->next=now;
}while(1);
return fake->next;
}
圖解:
- 3個指針變量控制,front,now,tail 前中後
- 把now取下後頭插到fake後面,循環重複本次操作,當now==NULL時完成逆置。
完結
總結:(待續)