C++指针和链表

来源:互联网 发布:印刷设计图像软件 编辑:程序博客网 时间:2024/05/18 00:30
转载 http://www.cnblogs.com/xuyuan77/archive/2008/03/29/1129295.html

How to create Linked list using C/C++

用C/C++创建链表

Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

说明:本文未经校注。如果文中有不恰当之处,请注意或拷贝其原创作品并提交文章。

·         Download Linked_list - 2.52 KB

Introduction

介绍

Linked list is one of the fundamental data structures, and can be used to implement other data structures. In a linked list there are different numbers of nodes. Each node is consists of two fields. The first field holds the value or data and the second field holds the reference to the next node or null if the linked list is empty.

链表是基本的数据结构之一,并常用来实现其他的数据结构。一个链表有不同的节点构成,每个节点又由两个域构成。第一个域存储数据和值,第二个域则保存指向向下一节点的引用,但若其无下一节点时,则存储NULL

 
Figure: Linked list

图:链表

Pseudocode:

伪代码:

Linkedlist Node {

    data // The value or data stored in the node

    next // A reference to the next node, null for last node

 }

The singly-linked list is the easiest of the linked list, which has one link per node.

链表中最简单的是单链表,其每个节点只有一个链接。

Pointer

指针

To create linked list in C/C++ we must have a clear understanding about pointer. Now I will explain in brief what is pointer and how it works.

要能够用C/C++创建链表,首先我们得对指针有个清晰的理解。下面我就简要介绍一下什么是指针,它又是如何运作的。

A pointer is a variable that contains the address of a variable. The question is why we need pointer? Or why it is so powerful? The answer is they have been part of the C/C++ language and so we have to use it. Using pointer we can pass argument to the functions. Generally we pass them by value as a copy. So we cannot change them. But if we pass argument using pointer, we can modify them. To understand about pointers, we must know how computer store variable and its value. Now, I will show it here in a very simple way.

指针就是包含有变量地址的变量。问题是为什么我们需要指针呢?因为其是C/C++语言的一部分,所以我们不得不用它(译者注:关于这个解释有点牵强)。我们可以通过指针传参数给函数。一般情况下,我们是传递变量值的一份拷贝,因而无法改变变量的值。但若我们通过指针进行传参,我们就可以修改参数的值了。为了很好的理解指针,我们就需要知道计算机是如何存储变量和变量的值的。下面让我用一种简单的方式来说明一下。

Let us imagine that a computer memory is a long array and every array location has a distinct memory location.

我们可以把计算机的内存想象成很长的数组,每个数组在内存中都有明确的位置。

 int a = 50// initialize variable a


Figure: Variable value store inside an array

图:存储在数组中的变量值

It is like a house which has an address and this house has only one room. So the full address is-

这就好比每个房子都有一个门牌号地址,并且有一个房间,所以完整的地址是-

Name of the house: a

Name of the person/value who live here is: 50

House Number: 4010

房子名称:a

住在这个房子里的人(值)是:50

门牌号为:4010

If we want to change the person/value of this house, the conventional way is, type this code line

如果我们想改变住在房子里面的人(值),下面的代码行就是最简单的办法

 a = 100    // new initialization

But using pointer we can directly go to the memory location of 'a' and change the person/value of this house without disturbing ‘a’. This is the main point about pointer.

通过指针,我们可以直接找到‘a’的内存位置,然后不影响‘a’的情况下改变它。这就是指针主要的一点。

Now the question is how we can use pointer. Type this code line:

那我们又如何使用指针呢?敲下下面的代码行:

 int *b;    // declare pointer b

We transfer the memory location of a to b.

我们把a在内存中的地址赋给b

 b = &a;    // the unary operator & gives the address of an object


Figure: Integer pointer 
b store the address of the integer variable a

整型指针b存储了整型变量a的地址

Now, we can change the value of a without accessing a.

下面,我们可以不用访问a而改变其值。

 *b = 100// change the value of 'a' using pointer ‘b’

 cout<<a; // show the output of 'a'

When you order the computer to access to access *b, it reads the content inside b, which is actually the address of a then it will follow the address and comes to the house of a and read a`s content which is 50.

当你用计算机指令间接访问*b时,它读的是b(即a所在地址)存储的具体内容,也就是根据a的地址来到房屋a,然后读取a的值50

Now the question is, if it is possible to read and change the content of b without accessing b? The answer is affirmative. We can create a pointer of pointer.

现在的问题是,如果可能不访问b而读取并改变了b的内容呢?答案是肯定的,因为我们可以创建指针的指针。

 int **c;   //declare a pointer to a pointer

 c = &b;    //transfer the address of ‘b’ to ‘c’

So, we can change the value of a without disturbing variable a and pointer b.

所以,我们甚至可以不通过ab而改变a的值。

 **c = 200// change the value of ‘a’ using pointer to a pointer ‘c

注:原文为*c=200**c是我根据理解修改的。

 cout<<a; // show the output of a

Now the total code is:

总的代码如下:

#include<iostream>

usingnamespace std;

 

int main()

{

      int a = 50;       // initialize integer variable a

      cout<<"The value of 'a': "<<a<<endl; // show the output of a

 

      int * b;          // declare an integer pointer b

      b = &a;           // transfer the address of 'a' to pointer 'b'

      *b = 100;         // change the value of 'a' using pointer 'b'

      cout<<"The value of 'a' using *b: "<<a<<endl;// show the output of a

 

      int **c;          // declare an integer pointer to pointer 'c'

      c = &b;           // transfer the address of 'b' to pointer to pointer 'c'

      **c = 200;        // change the value of 'a' using pointer to pointer 'c'

      cout<<"The value of 'a' using **c: "<<a<<endl;// show the output of a

 

      return0;

}

Output:

输出:


This program will give you the inside view of the pointer.

以下的程序将给你展示指针的内部情况。

#include<iostream>

usingnamespace std;

 

int main()

{

      int a = 50;       // initialize integer variable a

      cout<<"Value of 'a' = "<<a<<endl;          // show the output of a

      cout<<"Memory address of 'a': "<<&a<<endl; // show the address of a

      cout<<endl;

 

      int * b;             // declare an integer pointer b

      b = &a;              // transfer the address of 'a' to pointer 'b'

      cout<<"Value of Pointer 'b': "<<*b<<endl; // show the output of *b

      cout<<"Content of Pointer 'b': "<<b<<endl; // show the content of *b

      cout<<"Memory address of Pointer 'b': "<<&b<<endl; // show the address of *b

      cout<<endl;

 

      int **c;                // declare an integer pointer to a pointer

      c = &b;                 // transfer the address of 'b' to 'c'

      cout<<"Value of Pointer 'c': "<<**c<<endl; // show the output of **c

      cout<<"Content of Pointer 'c': "<<c<<endl; // show the content of **c

      cout<<"Memory address of Pointer 'c': "<<&c<<endl; // show the address of **c

      cout<<endl;

      return0;

}

Output:

输出:


We can observe that the memory address of 
a and the content of pointer b is same. The content of pointer and the memory address of b is same.

从结果我们可以看出,a的内存地址和指针b的内容是相同的,而指针c的内容又与b的内存地址一样。

Linked list operation

链表操作

Now we have a clear view about pointer. So we are ready for creating linked list.

现在我们已经清楚地了解了指针,下面我们就准备创建链表了。

Linked list structure

链表结构

typedefstruct node                                               

{                                                               

      int data;               // will store information

      node *next;             // the reference to the next node

};                                                              

First we create a structure “node”. It has two members and first isint data which will store the information and second is node *next which will hold the address of the next node. Linked list structure is complete so now we will create linked list. We can insert data in the linked list from 'front' and at the same time from 'back’. Now we will examine how we can insert data from front in the linked list.

首先我们创建一个“node”结构。它有两个成员,一个是整型变量data用来存储信息,而另一个是node指针变量next,其存储的是下一节点的地址。链表结构已完成,接着我们就来创建链表。我们可以同时在链表的首尾插入数据,下面就来测试一下如何链表的首部插入数据。

1)         Insert from front

从前插入

At first initialize node type.

首先初始化node类型的空链表head

node *head = NULL;             //empty linked list

Then we take the data input from the user and store in the node info variable. Create a temporary node node *temp and allocate space for it.

然后我们把用户输入的数据存储进node的信息变量。创建一个临时node指针*temp,并为其分配相应的存储空间。

node *temp;             //create a temporary node

temp = (node*)malloc(sizeof(node)); //allocate space for node

Then place info to temp->data. So the first field of the node *temp is filled. Now temp->next must become a part of the remaining linked list (although now linked list is empty but imagine that we have a 2 node linked list and head is pointed at the front) So temp->next must copy the address of the *head (Because we want insert at first) and we also want that *head will always point at front. So *head must copy the address of the node *temp.

信息的位置就是temp->data。所以node *temp的第一个域被填满,temp->next必须是剩余链表的一部分(尽管现在链表还是空的,但我们可以想象它有两个节点,head指向其头部),所以temp->next必须拷贝一份*head的地址(因为我们想先插入),接着我们又需要*head永远指向链表的开始位置,所以我们又必须把*temp的的地址拷贝给*head.


Figure: Insert at first

图:从头插入

temp->data = info;             // store data(first field)

temp->next=head; // store the address of the pointer head(second field)

head = temp;                  // transfer the address of 'temp' to 'head'

2)         Traverse

移动节点

Now we want to see the information stored inside the linked list. We create node *temp1. Transfer the address of *head to *temp1. So *temp1 is also pointed at the front of the linked list. Linked list has 3 nodes.

现在我们想看一下存储在链表中的信息。我们创建一个临时指针*temp。首先把链表头*head的地址赋给*temp1,这样*temp1就指向了链表的首部,链表有3个节点。

We can get the data from first node using temp1->data. To get data from second node, we shift *temp1 to the second node. Now we can get the data from second node.

通过temp1->data我们可以得到第一个节点的值。为了得到第二个节点的数据,我们需要移动*temp1指向第二个节点。然后就可以获得第二个节点的数据。

while( temp1!=NULL )

{

 cout<< temp1->data<<" ";// show the data in the linked list

 temp1 = temp1->next;   // tranfer the address of 'temp->next' to 'temp'

}


Figure: Traverse

This process will run until the linked list’s next is NULL.

当碰到链表的next为空时,该过程才会结束。

3)         Insert from back

从后插入

Insert data from back is very similar to the insert from front in the linked list. Here the extra job is to find the last node of the linked list.

数据从后插入数据从前插入数据非常相似,只是这里需要做额外的工作,找到链表的最后一个节点。

node *temp1;                         // create a temporary node

temp1=(node*)malloc(sizeof(node));   // allocate space for node

temp1 = head;                  // transfer the address of 'head' to 'temp1'

while(temp1->next!=NULL) // go to the last node

      temp1 = temp1->next;//tranfer the address of 'temp1->next' to 'temp1'

Now, Create a temporary node node *temp and allocate space for it. Then place info to temp->data, so the first field of the node node *temp is filled. node *temp will be the last node of the linked list. For this reason, temp->next will be NULL. To create a connection between linked list and the new node, the last node of the existing linked list node *temp1`s second field temp1->next is pointed to node *temp.

同样创建临时node指针*temp并为其分配内存,这样信息就是temp->data,所以node* temp的第一个域就被填满了。Node *temp最终是链表的最后一个节点,基于这一点,temp->next将为NULL。为了创建链表和新节点之间的链接,链表中的最后一个节点node *temp1的第二个域temp1->next将指向node *temp


Figure: Insert at last

node *temp;                           // create a temporary node

temp = (node*)malloc(sizeof(node)); // allocate space for node

temp->data = info;                   // store data(first field)

temp->next = NULL;                   // second field will be null(last node)

temp1->next = temp;                  // 'temp' node will be the last node

4)         Insert after specified number of nodes

在指定的节点位置插入

Insert data in the linked list after specified number of node is a little bit complicated. But the idea is simple. Suppose, we want to add a node after 2nd position. So, the new node must be in 3rd position. The first step is to go the specified number of node. Let, node *temp1 is pointed to the 2nd node now.

在链表中的指定节点位置插入数据稍微有一小点复杂。但思路很简单。假设我们要在第二个节点之后加入一个节点,如此,新的节点就必须处于第3的位置。首先就是要找到指定位置的节点,让node *temp1指向第二个节点。

cout<<"ENTER THE NODE NUMBER:";

cin>>node_number;                   // take the node number from user

 

node *temp1;                        // create a temporary node

temp1 = (node*)malloc(sizeof(node)); // allocate space for node

temp1 = head;

 

forint i = 1 ; i < node_number ; i++ )

{

      temp1 = temp1->next;           // go to the next node

 

      if( temp1 == NULL )

      {

            cout<<node_number<<" node is not exist"<< endl;

            break;

      }

}

Now, Create a temporary node node *temp and allocate space for it. Then place info to temp->next , so the first field of the node node *tempis filled.

现在,创建一个临时节点node *temp 并未其分配内存空间,把info赋给temp->next,这样节点node *temp的第一个域就有了值。

node *temp;                          // create a temporary node

temp = (node*)malloc(sizeof(node)); // allocate space for node

temp->data = info;                   // store data(first field)

To establish the connection between new node and the existing linked list, new node’s next must pointed to the 2nd node’s (temp1) next . The 2nd node’s (temp1) next must pointed to the new node(temp).

为了在新节点和已存在的链表之间建立链接,新节点的next必须指向第二个节点(temp1)next。这样第二个节点(temp1)的next就必须指向新节点(temp)。

temp->next = temp1->next;            //transfer the address of temp1->next to temp->next

temp1->next = temp;     //transfer the address of temp to temp1->next

                 


Figure: Insert after specified number of nodes

图:在指定节点处插入

5)         Delete from front

从前删除

Delete a node from linked list is relatively easy. First, we create node *temp. Transfer the address of *head to *temp. So *temp is pointed at the front of the linked list. We want to delete the first node. So transfer the address of temp->next to head so that it now pointed to the second node. Now free the space allocated for first node.

从链表中删除一个节点相对来说比较简单。首先,我们创建一个节点*temp。把*head的地址赋给*temp。这样*temp就指向了链表的头。我们想删除第一个节点,所以就要把temo->next的地址赋给head,这样它(head)就指向了第二个节点,现在就可以释放第一个节点所占用的空间了。

node *temp;                                      // create a temporary node

temp = (node*)malloc(sizeof(node)); // allocate space for node

temp = head;                   // transfer the address of 'head' to 'temp'

head = temp->next;      // transfer the address of 'temp->next' to 'head'

free(temp);


Figure: Delete at first node

图:删除第一个节点

6)         Delete from back

从后删除

The last node`s next of the linked list always pointed to NULL. So when we will delete the last node, the previous node of last nodeis now pointed at NULL. So, we will track last node and previous node of the last node in the linked list. Create temporary node * temp1 and *old_temp.

链表最后一个节点的next总是指向NULL,所以我们要删除最后一个节点,就需要最后一个节点之前的那个节点的next指向NULL。我们将通过链表最后一个节点和其之前的那个节点,创建一个临时节点*temp1.

// create a temporary node

node *temp1;

temp1 = (node*)malloc(sizeof(node)); // allocate space for node

temp1 = head;                        //transfer the address of head to temp1

node *old_temp;                     // create a temporary node

old_temp = (node*)malloc(sizeof(node));    // allocate space for node

 

while(temp1->next!=NULL)             // go to the last node

{

      old_temp = temp1; // transfer the address of 'temp1' to 'old_temp'

      temp1 = temp1->next;       // transfer the address of 'temp1->next' to 'temp1'

}

Now node *temp1 is now pointed at the last node and *old_temp is pointed at the previous node of the last node. Now rest of the work is very simple. Previous node of the last node old_temp will be NULL so it become the last node of the linked list. Free the space allocated for last lode.

现在节点*temp1指向了最后一个节点,而*old_temp就指向了倒数第二个节点。剩下的工作就非常简单了。倒数第二个节点old_temp将指向NULL从而成为链表最后一个节点,然后释放原最后一个节点的空间。

old_temp->next = NULL;         // previous node of the last node is null

free(temp1);


Figure: Delete at first last

7)         Delete specified number of node

删除指定位置的节点。

To delete a specified node in the linked list, we also require to find the specified node and previous node of the specified node. Create temporary node * temp1*old_temp and allocate space for it. Take the input from user to know the number of the node.

要删除链表中指定位置的节点,我们仍然需要找到指定的节点和其之前的节点。创建临时节点*temp1*old_temp并为它们分配相应的存储空间。从用户的输入来得到指定节点的序号。

node *temp1;                         // create a temporary node

temp1 = (node*)malloc(sizeof(node)); // allocate space for node

temp1 = head;                  // transfer the address of 'head' to 'temp1'

 

node *old_temp;                     // create a temporary node

old_temp = (node*)malloc(sizeof(node));    // allocate space for node

old_temp = temp1;       // transfer the address of 'temp1' to 'old_temp'

cout<<"ENTER THE NODE NUMBER:";

cin>>node_number;                    // take location

forint i = 1 ; i < node_number ; i++ )

{

      old_temp = temp1;                    // store previous node

      temp1 = temp1->next;                 // store current node

 

}

Now node *temp1 is now pointed at the specified node and *old_temp is pointed at the previous node of the specified node. The previous node of the specified node must connect to the rest of the linked list so we transfer the address of temp1->next to old_temp->next. Now free the space allocated for the specified node.

现在节点*temp1指向了指定的节点,而*old_temp则指向了之前的那个节点。之前的那个节点必须链接链表的剩余部分,所以我们需要把temp->next的地址赋给old_temp_next。最后再释放指定节点所分配的存储空间。

old_temp->next = temp1->next; // transfer the address of 'temp1->next' to 'old_temp->next'

free(temp1);

8)         Sort nodes

节点排序

Linked list sorting is very simple. It is just like ordinary array sorting. First we create two temporary node node *temp1*temp2 and allocate space for it. Transfer the address of first node to temp1 and address of second node to temp2. Now check if temp1->data is greater than temp2->data. If yes then exchange the data. Similarly, we perform this checking for all the nodes.

链表排序非常简单。就像普通数组排序一样。首先我们要创建两个临时节点*temp1,*temp2并为它们分配存储空间。然后把第一个节点的地址赋给temp1,把第二个节点的地址赋给temp2.然后比较temp1->datatemp2->data。如果前者大则交互数据。以此类推,我们如此检查所有的节点。

node *temp1;                         // create a temporary node

temp1 = (node*)malloc(sizeof(node)); // allocate space for node

 

node *temp2;                         // create a temporary node

temp2 = (node*)malloc(sizeof(node)); // allocate space for node

 

int temp = 0;                        // store temporary data value

 

for( temp1 = head ; temp1!=NULL ; temp1 = temp1->next )

{

      for( temp2 = temp1->next ; temp2!=NULL ; temp2 = temp2->next )

      {

            if( temp1->data > temp2->data )

            {

                  temp = temp1->data;

                  temp1->data = temp2->data;

                  temp2->data = temp;

            }

      }

}

Conclusion

总结

From the above discussions, I hope that everybody understands what linked list is and how we can create it. Now we can easily modify linked list according to our program requirement and try to use it for some real tasks. Those who still have some confusion about linked list, for them I will now tell you a story.

通过上述讨论,我希望大家都能明白什么是链表以及如何创建它。根据我们的程序需求,我们现在可以简单的修改链表并能够用其实现一些真正的任务。对于那些仍然还对链表感到混淆不清的人,这里我将告诉你们一个故事。

Once upon a time, an old man lived in a small village. The old man was very wise and knew many solutions about different problems. He had also special powers so he could talk to genie and spirits, and sometimes they granted his wish by using their special powers. Oneday a witch with a broom came to talk with him and ask difficult and complex issues about global warming. He was very surprised but patiently explained her about green house model and gave her advice about using biofuel in her broom. The witch was very rude and greedy but she always liked to preach her nobility. So at the time of her departure, she wanted to grant only two wishes. The old man asked her why she only granted two wishes. He also reminded her that whenever genie comes he granted two wishes.” What I am look like" the witch asked angrily,” A blank check?" The old man brewed a plan. He told her that his first wish was to get a super computer.” It is granted", the witch announced loudly.” Then my second wish is to have another two wishes”, the old man said very slowly. The witch was shell shocked. "It is also granted”, the witch said and left the place very quickly with her broom.

从前,有位老者居住在一个小村庄里,老者充满智慧,能够解答许多不同的问题。他有能够跟幽默鬼怪对话的特异功能。有时,妖魔鬼怪会愿意通过他们的法力来满足他的愿望。有一天来了一个带着扫把的巫婆,巫婆问了他一些关于全球变暖的难并复杂的问题。老者感到非常惊讶,但还是耐心地为巫婆解释了温室效应并给了一些如何利用她扫帚的建议。可巫婆非常的粗鲁和贪婪,总是喜欢吹嘘她的高贵。所以在她临走时,巫婆说仅可以帮老者实现仅有的两个愿望。老者问巫婆,为什么只两个愿望呢,并提醒她说,任何时候妖怪来了,都会给老者两个愿望的。“我长得怎么样?”巫婆生气的问道,“是空头支票吗?”。老者心中有了盘算,然后告诉巫婆,他的第一个愿望是得到一个超级计算机。“这个已经实现了”,巫婆大声吼道。“我第二个愿望就是还想要另外的两个愿望”,老者慢慢的说。巫婆顿时感到震惊,说“那个也实现了”。说罢就驾着她的扫帚飞速离去了。

You may ask yourself why the witch was surprised. First, the witch granted two witches. One was fulfilled and for the second wish, the old man wanted another two wish. “What’s the big idea?” you can ask me,” The witch can also fulfill this wish”. Certainly, the witch can grant his wish. But what will happen if the old man wants to extend his second wish to another two wish set. So, the process will never end unless, the old man want to stop. The idea is same for linked list. First you have a node where you can imagine that data is the first wish and node*next is the second wish by which you can create second node just like first. This process will continue until you put NULL in the *next.

你可能会问,为什么巫婆会感到惊讶。首先,巫婆实现了两个愿望。一个已经实现了,而对于第二个愿望,老者则是还想要另两个愿望。“那建议是什么呢?”你可能会问我,“那个巫婆依然可以实现这个愿望”。可以肯定,巫婆可以实现他的愿望,但如果老者再一次要求第二个愿望是实现另外的两个愿望呢。所以,这个过程是无休无止的。除非老者想停止。这个想法恰好和链表是如此的相似。首先,你有一个节点,你可以把数据想象成第一个愿望,而node* next则是第二个愿望,你可以用它创建一个和第一个节点一样的节点,这个过程只有再你把NULL赋给*next时才会终止。


Figure: It looks like the old man had lots of wish.

图:它就像老者要求有很多的愿望一样

References

参考书籍:

  1. Wikipedia, the free encyclopedia.
  2. Pointer in C, from P. Kanetkar.

License

许可

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

本文及其相关的代码和文档由The Code Project Open License (CPOL)授权许可。

About the Author

关于作者

Nasif M.

程序下载地址:http://www.codeproject.com/KB/cpp/linked_list/Linked_list.zip

文章出处:http://www.codeproject.com/KB/cpp/linked_list.aspx

0 0
原创粉丝点击