链表

1.链表

链表是一种常见的数据结构,在物理存储上是非连续非顺序的,数据的逻辑顺序是通过指针连接。链表的结点一般由存储数据元素的数据域指向下一个结点地址指针域构成。

struct node{
	typename data;
	node *next;
}

链表可以分为带头结点的链表和不带头结点的链表,这里讲的是带头结点的链表。头结点一般称为head,数据域无内容,指针域指向第一个结点。最后一个结点的指针指向NULL,表示链表的结尾。

2.结点内存空间的分配

(1)malloc
malloc函数是stdlib.h下的动态内存分配的函数,定义如下:

typename *p=(typename *p)malloc(sizeof(typename));

该函数申请一个大小为sizeof(typename)的空间并返回指向这块空间的和变量相同类型的指针,因此前面要加上一个强制类型转换。申请成功就可以使用指针p访问,申请失败的话返回NULL(申请较大动态数组可能会失败)。
(2)new
new是动态申请空间的运算符,返回类型同样是指针。推荐使用这种写法,相较于malloc简洁得多。如果申请较大的数组可能会申请失败,会产生异常。

typename *p=new typename;

(3)free/delete
如果申请的内存空间在使用完毕后没有及时释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃,这个问题称为系统泄漏。可见及时释放空间是很有必要的。
释放内存空间的两种方法:
1.free
free函数用于释放malloc函数开辟的空间,两者必须成对出现。

free(p)    //p为需要释放的空间的指针变量

2.delete
delete函数用于释放new运算符开辟的空间。

delete(p)   //p为需要释放的空间的指针变量

3.链表的基本操作

(1)创建链表
将一个数组连接成链表

#include <stdio.h>

struct node{
    int data;
    node *next;
};

node *create(int a[]){
    node *head=new node;
    head->next=NULL;
    node *p,*pre;
    pre=head;
    for(int i=0;i<5;i++){
        p=new node;
        p->data=a[i];
        p->next=NULL;
        pre->next=p;
        pre=p;
    }
    return head;
}

int main(){
    int a[5]={1,2,3,4,5};
    node *head=create(a);
    node *p=head->next;
    while(p!=NULL){
        printf("%d ",p->data);    //1 2 3 4 5
        p=p->next;
    }
    return 0;
}

(2)查找元素

//查找元素x,若链表中存在x返回1,否则返回0
int find(node *head,int x){
    node *p=head->next;
    while(p!=NULL){
        if(p->data==x)
            return 1;
        p=p->next;
    }
    return 0;
}

(3)插入元素

//在第pos个结点处插入元素x
void insert(node *head,int pos,int x){
    node *p=new node;
    p->data=x;
    p->next=NULL;
    node *q=head->next;
    for(int i=1;i<pos;i++){
        q=q->next;
    }
    p->next=q->next;
    q->next=p;
}

(4)删除元素

//删除数据为x的结点
void del(node *head,int x){
    node *p=head->next;
    node *pre=head;
    while(p!=NULL){
        if(p->data==x){
            pre->next=p->next;
            delete(p);
            p=pre->next;
        }
        else{
            pre=p;
            p=p->next;
        }
    }
}

完整代码:

#include <stdio.h>

struct node{
    int data;
    node *next;
};
  
//创建链表
node *create(int a[]){
    node *head=new node;
    head->next=NULL;
    node *p,*pre;
    pre=head;
    for(int i=0;i<5;i++){
        p=new node;
        p->data=a[i];
        p->next=NULL;
        pre->next=p;
        pre=p;
    }
    return head;
}

//查找
int find(node *head,int x){
    node *p=head->next;
    while(p!=NULL){
        if(p->data==x)
            return 1;
        p=p->next;
    }
    return 0;
}

//插入
void insert(node *head,int pos,int x){
    node *p=new node;
    p->data=x;
    p->next=NULL;
    node *q=head->next;
    for(int i=1;i<pos;i++){
        q=q->next;
    }
    p->next=q->next;
    q->next=p;
}

//删除
void del(node *head,int x){
    node *p=head->next;
    node *pre=head;
    while(p!=NULL){
        if(p->data==x){
            pre->next=p->next;
            delete(p);
            p=pre->next;
        }
        else{
            pre=p;
            p=p->next;
        }
    }
}

int main(){
    int a[5]={1,2,3,4,5};
    node *head=create(a);
    node *p=head->next;
    while(p!=NULL){
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
    printf("%d\n",find(head,2));    //查找元素2
    insert(head,3,3);    //在第三个结点插入元素3
    p=head->next;
    while(p!=NULL){
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
    del(head,3);    //删除值为3的结点
    p=head->next;
    while(p!=NULL){
        printf("%d ",p->data);
        p=p->next;
    }
    return 0;
}

运行结果:

1 2 3 4 5
1
1 2 3 3 4 5
1 2 4 5

4.静态链表

如果结点的地址都比较小,便没有必要再去使用动态链表,用基于hash原理的静态链表会方便很多。建立如下的结构体数组:

struct Node{
	typename data;    //数据域
	int next;    //指针域
}node[N];

其中数组的下标就是该结点的地址next是一个整数,用于存放下一个结点的地址(和动态链表中的next指针作用一致),最后一个结点的next值为-1(相当于动态链表p->next=NULL),表示链表的末尾。静态链表可以直接访问通过地址访问数组中的元素来访问结点,因此是不需要头结点的。在使用静态链表时最好把结构体名和结构体变量名定义成不一样。

实例1:
PAT 1032 sharing

To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For example, loading and being are stored as showed in Figure 1.
在这里插入图片描述
Figure 1
You are supposed to find the starting position of the common suffix (e.g. the position of i in Figure 1).

Input Specification:
Each input file contains one test case. For each case, the first line contains two addresses of nodes and a positive N (≤10^5), where the two addresses are the addresses of the first nodes of the two words, and N is the total number of nodes. The address of a node is a 5-digit positive integer, and NULL is represented by −1.
Then N lines follow, each describes a node in the format:

Address Data Next

where Address is the position of the node, Data is the letter contained by this node which is an English letter chosen from { a-z, A-Z }, and Next is the position of the next node.

Output Specification:
For each case, simply output the 5-digit starting position of the common suffix. If the two words have no common suffix, output -1 instead.

Sample Input 1:

11111 22222 9
67890 i 00002
00010 a 12345
00003 g -1
12345 D 67890
00002 n 00003
22222 B 23456
11111 L 00001
23456 e 67890
00001 o 00010

Sample Output 1:

67890

Sample Input 2:

00001 00002 4
00001 a 10001
10001 s -1
00002 a 10002
10002 t -1

Sample Output 2:

-1

题意:两条链表分别存储两个英文单词,一个结点对应一个字母。输入的三个整数分别是第一条链表首结点地址、第二条链表首元素地址、结点的个数。判断两条链表是否存在共用的结点,存在则输出第一个共用结点的地址,否则输出-1。

思路:根据题意结点的地址为五位整数,范围很小,因此使用静态链表求解。在Node结构体中除了data和next,还需要定义一个flag用于标识该结点是否出现在第一条链表中。链表初始化和输入都很常规,接着遍历一遍第一条链表,将所有结点的flag赋值为true,随后遍历第二条链表,如果某个结点的flag值为true说明找到了两条链表的第一个共用结点。

注意点:输出时要以%05d的格式输出(测试点4)

AC代码:

#include <stdio.h>

const int maxn=100000;
struct Node{
    char data;
    int next;
    bool flag;    
}node[maxn];

int main(){
    int a,b,n;
    scanf("%d %d %d",&a,&b,&n);
    for(int i=0;i<maxn;i++){
        node[i].flag=false;
        node[i].next=-1;
    }
    for(int i=0;i<n;i++){
        int This,Next;
        char c;
        scanf("%d %c %d",&This,&c,&Next);
        node[This].data=c;
        node[This].next=Next;
    }
    int t=a;
    while(t!=-1){
        node[t].flag=true;
        t=node[t].next;
    }
    t=b;
    int flag=-1;
    while(t!=-1){
        if(node[t].flag==true){
            printf("%05d\n",t);
            flag=1;
            break;
        }
        t=node[t].next;
    }
    if(flag==-1)
        printf("-1\n");
    return 0;
}

实例2:
PAT 1052 Linked List Sorting

A linked list consists of a series of structures, which are not necessarily adjacent in memory. We assume that each structure contains an integer key and a Next pointer to the next structure. Now given a linked list, you are supposed to sort the structures according to their key values in increasing order.

Input Specification:
Each input file contains one test case. For each case, the first line contains a positive N (<10^5​) and an address of the head node, where N is the total number of nodes in memory and the address of a node is a 5-digit positive integer. NULL is represented by −1.

Then N lines follow, each describes a node in the format:

Address Key Next

where Address is the address of the node in memory, Key is an integer in [−10^5 , 10^5], and Next is the address of the next node. It is guaranteed that all the keys are distinct and there is no cycle in the linked list starting from the head node.

Output Specification:
For each test case, the output format is the same as that of the input, where N is the total number of nodes in the list and all the nodes must be sorted order.

Sample Input:

5 00001
11111 100 -1
00001 0 22222
33333 100000 11111
12345 -1 33333
22222 1000 12345

Sample Output:

5 12345
12345 -1 00001
00001 0 11111
11111 100 22222
22222 1000 33333
33333 100000 -1

题意:给定一个链表,其中每个结点是一个包含key和next的结构体,将这个链表按照结点key的大小升序排序。

思路:结点地址都为五位数,依然采用静态链表,结构体中除了key和next,还需要定义一个address(结点本身的地址,排完序过后下标不再是原来的地址了,所以需要将地址保存起来),和一个flag(用于标记结点是否在链表中)。将数组中所有结点的flag值置为-1,遍历链表将链表中所有结点的flag值置为1。随后用sort排序,cmp函数需要有第二级排序,如果两个结点中有一个不在链表中就按照flag值降序排序(目的是将链表中的结点都放到数组的前面),如果两个结点都在链中就根据题意按照key的大小降序排列即可。

注意点:
1.地址要按照%05d格式输出,但是尾结点的next就是输出-1。
2.输入样例中可能存在无效结点,即输入的结点并不是链表中的结点,需进行排除。
3.所有输入结点均无效的特殊情况要单独处理。

AC代码:

#include <stdio.h>
#include <algorithm>
using namespace std;

const int maxn=100000;
struct Node{
    int key;
    int next;
    int flag;
    int address;
}node[maxn];

bool cmp(Node a,Node b){
    if(a.flag==-1 || b.flag==-1)
        return a.flag>b.flag;
    else
        return a.key<b.key;
}

int main(){
    int n,a;
    scanf("%d %d",&n,&a);
    for(int i=0;i<maxn;i++)
        node[i].flag=-1;
    for(int i=0;i<n;i++){
        int address,key,next;
        scanf("%d %d %d",&address,&key,&next);
        node[address].key=key;
        node[address].next=next;
        node[address].address=address;
    }
    int t=a;
    int num=0;
    while(t!=-1){
        node[t].flag=1;
        num++;
        t=node[t].next;
    }
    if(num==0)
        printf("0 -1");
    else{
        sort(node,node+maxn,cmp);
        printf("%d %05d\n",num,node[0].address);
        for(int i=0;i<num;i++){
            if(i<num-1)
                printf("%05d %d %05d\n",node[i].address,node[i].key,node[i+1].address);
            else
                printf("%05d %d -1\n",node[i].address,node[i].key);
        }
    }
    return 0;
}

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页