链表

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。从存储数据集和得角度讲,链表和数组是很相识的。

数组也许是最常见的数据组织形式,在很多编程语言中,他的定义和使用都是相当方便的,如下我们定义一个数组,并初始化前三个元素。

void ArrayTest() {

int scores[100];

scores[0] = 1;

scores[1] = 2;

scores[2] = 3;

}

在内存表示形式上,数组的表示大致如下:

一旦数组建立以后,就很容易通过下标访问他的任意数据元素。访问数组时如:score[2],就是访问的第三个元素。即使如此,数组有以下缺点:

1,  数组的大小(元素个数)是固定的,不可变的;在进行编译时,就会根据数组的定义来分配固定大小的内存空间。

2,  正是由于问题一得存在,我们在声明时,就会尽量的申请更大的空间,但是偶尔我们的空间还是不够的,如:我们要是存score[101]?

3,  要把一个数据插入到数组的开头,是不是相当麻烦?所有的元素向后走一步?

然而链表的产生正是弥补了数组的种种缺陷,相比较数组要分配固定的连续大小的空间,链表的元素在内存空间分配上可以使不连续的。

基础知识:指针,malloc(),free()

链表的组织形式如下图:

一般链表有表头和身体组成,我们在创建链表时。用malloc为链表在heap上分配空间,然后linklist得每一个元素是以node的形式组织的:数据域和指针域。

如果是空链表,那么首指针就是指向NULL,在进行链表操作时,一定要考虑链表是否为空。

链表的节点和节点指针:

节点的定义:

struct node {

int data;//存储数据;

struct node* next;//存储指向下一个节点的指针;

};

下面范例链表的创建:

struct node* BuildOneTwoThree() {

struct node* head = NULL;

struct node* second = NULL;

struct node* third = NULL;

head = malloc(sizeof(struct node)); // allocate 3 nodes in the heap

second = malloc(sizeof(struct node));

third = malloc(sizeof(struct node));

head->data = 1; // setup first node

head->next = second; // note: pointer assignment rule

second->data = 2; // setup second node

second->next = third;

third->data = 3; // setup third link

third->next = NULL;

return head;

}

//上例的等效创建方法:

struct node * addnode(struct node *head,int data)

{

  struct node* tmp;

  if(head==null)

{

head=(struct node *)malloc(sizeof(struct node));

if(head==null){

printf(“create node failed”);

exit(0);

}

head->data=data;

head->next=head;

}

 else {

    tmp=head;

    while(tmp->next!=head)

tmp=tmp->next;

tmp=(struct node *)malloc(sizeof(struct node));

if(tmp==null)

{

printf(“malloc failed”);

exit(0);

}

else

{

tmp=tmp->next;

tmp->data=data;

tmp->next=head;

}

return head;

}

}

int main(int argc,char *argv[])

{

struct node *head=null;

head=addnode(head,1);

head=addnode(head,2);

head=addnode(head,3);

}

链表的长度:

用函数length()可以很容易的求出链表的元素个数;

int length(struct node *head)

{

struct node *current=head;//函数调用结束,current 自动被回收;

int count=0;

while(current->next!=NULL)

{

current=current->next;

count++;

}

return count;

}

上述函数很简单,但是有两点:

参数传递的是head指针------------------head node ;

        与局域指针进行交互;-------------------current node;

指针节点插入到链表的头部:

1,  malloc

//heap上创建node,然后令其data等于预定值,

newnode=mallocsizeof(struct node));

newnode->data=”data set”;

2,  next转变;

//newnode->next=head;

3,  改变头结点;

head=newnode;

《被调函数如果想改变主调函数的内存内容,很多途径:一,作为函数参数传递,然后返回该参数;地址传递(传递参数的地址,如果参数是指针类型的,那就是“**”型的)》

void Push(struct node** headRef, int data) {

struct node* newNode = malloc(sizeof(struct node));

newNode->data = data;

newNode->next = *headRef; // The '*' 指向了真实的主调函数相关内存;

*headRef = newNode; // ditto

}

void PushTest() {

struct node* head = BuildTwoThree();//Push(&head, 1); // note the &

Push(&head, 13);

}

实现链表的遍历步骤:

1,  定义遍历函数,参数是指向节点指针的指针,如:struct node **;

2,  &获取要改变节点的地址;主函数中调用遍历函数,参数书写为:&节点指针名

3,  在被调函数中用*来改变节点指针的值;

 

 

 

下面的函数使头结点指向NULL;

void ChangeToNull(struct node** headRef)

{

*headRef = NULL;

}

void ChangeCaller()

{

struct node* head1;

struct node* head2;

ChangeToNull(&head1);

ChangeToNull(&head2);

 }

在创建链表时一般采用头插法,结果是插入的数值将是倒叙的;下面例子实现的是尾插法

struct node* BuildWithSpecialCase() {

struct node* head = NULL;

struct node* tail;

int i;

Push(&head, 1);

tail = head;

for (i=2; i<6; i++) {

Push(&(tail->next), i);

tail = tail->next;

}

return(head); // head == {1, 2, 3, 4, 5};

}

在进行插入时,考虑到第一个元素采用头插法,而剩余的采用尾插法,简单的方法是假设在链表在插入前已经有一个虚假节点,那么就可以统一采用尾插法进行链表的建立;

struct node* BuildWithDummyNode() {

struct node dummy;

struct node* tail = &dummy;

int i;

dummy.next = NULL;

for (i=1; i<6; i++) {

Push(&(tail->next), i);

tail = tail->next;

}

return(dummy.next);

}

上述的虚假节点采用临时的处理方法,平时多用永久性的虚假节点;也有另外一种方法就是局部指针变量的方法:局部指针变量指向的链表的最后一个指针而不是最后一个节点;

struct node* BuildWithLocalRef() {

struct node* head = NULL;

struct node** lastPtrRef= &head;

int i;

for (i=1; i<6; i++) {

Push(lastPtrRef, i);

lastPtrRef= &((*lastPtrRef)->next); }

return(head);

}

上面的函数有点危险。。。。。。。。。。。。。。。。。。。。。。。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值