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

一 单链表之基数排序

书上讲的不是很清楚,而且书上建议用链表实现,我看了一段时间不知道怎么用链表实现。后来参考http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.htmlhttp://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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值