数据结构学习笔记3(链表 下 双向链表&单链表逆转)

来源:互联网 发布:手机剪辑mp3软件 编辑:程序博客网 时间:2024/04/30 01:43

一 单链表之基数排序

书上讲的不是很清楚,而且书上建议用链表实现,我看了一段时间不知道怎么用链表实现。后来参考http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.html和http://blog.csdn.net/feixiaoxing/article/details/6876831来对基数排序算法进行学习

但是包括维基百科和上面两个网站的内容,都不能经过测试:1负数的排序问题 2 没有用到链表,只是用到了数组 PS:书中也没有讲出到底哪里用到链表了 3在大量数据的情况下,时间复杂度不乐观。

呃,至于基数排序的实现,在排序 那里再战!

二 双向链表

双向链表与单链表在创建、验证是否空链表、使空、查找.etc方面都基本相同不同在于插入数据和删除数据,涉及到两个指针的处理。基本操作如下:

#include <stdio.h>#include <STDLIB.H>typedef Double_link_Node PtrToNode;typedef int ElementType;typedef struct Node{ PtrToNode Primer; ElementType Data; PtrToNode Next;}*Double_link_Node;bool InsertData(PtrToNode L,ElementType x,int n)//刚开始返回的是链表L的首地址,但是L的首地址不变无需返回;定义为bool变量来验证函数是否正确执行{ PtrToNode tmpCell,Position; int i; tmpCell=(PtrToNode)malloc(sizeof(struct Node)); Position=(PtrToNode)malloc(sizeof(struct Node)); Position=L; if(Position->Next==NULL) return false; for(i=0;i<n&&Position->Next!=NULL;i++)  Position=Position->Next;//此处也可以倒着插入 if(i<n-1)   return false;//说明链表L中含有少于n个元素 tmpCell->Data=x; tmpCell->Primer=Position; if (Position->Next!=NULL) {  tmpCell->Next=Position->Next;  Position->Next.Primer=tmpCell;  Position->Next=tmpCell; } else//恰好插入处是最后一个元素 {  tmpCell->Next=NULL;  Position->Next=tmpCell; } free(tmpCell); free(Position); return true;}bool DeleteData(PtrToNode L,ElementType x){ PtrToNode tmpCell,Position; int i; tmpCell=(PtrToNode)malloc(sizeof(struct Node)); Position=(PtrToNode)malloc(sizeof(struct Node)); Position=L; if(Position->Next==NULL) return false; while(Position->Next!=NULL&&Position->Next.Data!=x)  Position=Position->Next; if(Position->Next==NULL)   return false;//链表中没有x if (Position->Next.Next!=NULL&&Position!=L)//三种情况:1 找到x且位于链表中间  {  tmpCell=Position->Next.Next;  tmpCell->Primer=Position;  Position->Next=tmpCell; } else if (Position==L)// 2 x位于链表头 {  tmpCell=Position->Next.Next;  tmpCell->Primer=NULL;  L=tmpCell; }  else//3 x是链表最后一个  Position->Next=NULL; free(tmpCell); free(Position);  return true; }

三 循环单向链表

顾名思义,循环单向链表就是链表最后一个元素的Next指向链表的第一个元素,在操作中有一下不同

1查找、打印、计数等操作不是以==NULL作为终结条件,而是==L 即if(P->Next==NULL)

2对于插入操作,如果原来的链表IsEmpty,需要malloc空间,然后自己指向自己(这个没想到);如果原来的链表有Node,那么修改两个指针即可。

3对于删除操作,a如果只有这么一个节点,那么删除后返回NULL;b如果删除Node是头,那么要修改头


四 单链表逆转

链表逆转是面试环境中经常遇到的一道题目,也是我们在实际开发中可能会遇到的开发需求。

如果是数组逆转,就很方便实现,麻烦点的可以新建一个同样大小的数组进行赋值然后memmove;也可以直接用一个中间变量从数组中间交换对应数据。课后习题3.12a提出一个解法也很新颖,即把链表内容存入栈中,利用栈先进后出的特点对链表进行逆转。但是需要O(N)的extra space,且需要编写栈操作的例程,不划算。3.12b的答案就是下述用三个指针的方法。

链表逆转需要用到三个指针,分别记录当前节点和前后的两个节点。也可以用递归实现。给出两个实现方法。

#include <STDLIB.H>#include <STDIO.H>typedef struct Node *PtrToNode;struct Node{ PtrToNode Next; int data;};void ListReverse(PtrToNode List1,PtrToNode P);void ListReverse1(PtrToNode List1);int main(void){ PtrToNode List1,tmpCell,P; List1=(PtrToNode)malloc(sizeof(struct Node)); P=(PtrToNode)malloc(sizeof(struct Node)); List1->Next=NULL; P=List1; int i,n; for (i=0;i<5;i++) {  tmpCell=(PtrToNode)malloc(sizeof(struct Node));  tmpCell->data=10-i;  tmpCell->Next=P->Next;  P->Next=tmpCell; } ListReverse1(List1); P=List1->Next; for (i=0;i<5&&P!=NULL;i++) {  printf("%d  ",P->data);  P=P->Next; }  return 0;}void ListReverse1(PtrToNode List1){ PtrToNode P,P1,P2; P=List1->Next; P1=NULL;//!!这里需要对P1进行初始化为NULL;不然第一个元素的next依然指向第二个元素,会形成循环链表 while(P)  {   //P=P->Next;  P2=P->Next;  P->Next=P1;  P1=P;  P=P2; }  List1->Next=P1;//这里到最后P是无效链表,P1为倒序链表,需要用P1赋值 P=RecursiveReverse(List1,List1);//这是下文的递归实现的调用 P=List1->Next; while(P!=NULL) {  printf("%d  ",P->data);  P=P->Next; }  //return 0;}


(1不要眼高手低,任何小的程序都可能是一次考验 2程序出bug时,一定用调试来解决问题,手算不出来;对算法的基本原理要清楚)
链表逆转的递归实现
主要参考http://www.cnblogs.com/hiber/archive/2007/04/29/732293.html。但是该文中第一种递归调用调试有误,且不能清楚看懂递归的算法逻辑;第三种方法也是一种递归,但是调试过程中也出现错误,好在作者的基本思路比较清晰,进行变通后如下:


PtrToNode RecursiveReverse(Node *head,Node *List1){     struct Node *first;     struct Node *rest; struct Node *P=List1;     if(!(head))  return head;     first = head;     rest = first->Next;     if(!rest)//当first指向链表尾时递归结束 {  List1->Next=first;  return first; }     rest=RecursiveReverse(rest,List1);//递归每一个过程都是rest = first->Next,且几层递归中first指向所有的Node /*if(rest->Next==NULL)  List1->Next=rest; else   rest=rest->Next;*/ if(first==P) {  /*first->Next=NULL;*/  return rest; } rest->Next=first; first->Next=NULL; rest=rest->Next; return rest;      /*first->Next->Next = first;     first->Next = NULL;     head = rest;*/}

思路简介:在rest=RecursiveReverse(rest,List1);这句话之前是进行验证的步骤,运行到first指向链表尾时候,则所有的Node都已经被遍历,满足return要求,指针不再往后移动,这里把first赋值给List1->Next,是反向链表的开始处。List1这个参数也就在这里有用,这个递归思路比较简单,List1地址传递不能讨巧。在遍历完后,每一个递归过程的first分别指向各个不同的Node,从这点来说,有点浪费内存。rest的处理就是对逆序的链表进行链接,返回rest也是为了把整个表链接起来。P的作用是检测终结条件,在first==P时候说明逆序已经完全遍历一遍,如果不结束,此时first指向的是第二个节点,再对rest->Next赋值就形成了循环链表。
first->Next=NULL;很特殊,如果没有这句话,链表也可以正常逆转到最后一个Node。但是,此时first、P、List1的地址相等,即first此时指向List1,如果这里对first->Next赋值为NULL,链表也就为空;如果不对链表的最后一个Node->Next=NULL操作,则形成一个循环链表。


                                             
0 0
原创粉丝点击