一 单链表之基数排序
书上讲的不是很清楚,而且书上建议用链表实现,我看了一段时间不知道怎么用链表实现。后来参考http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.html和http://blog.youkuaiyun.com/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操作,则形成一个循环链表。