线性表:由零个或多个数据元素组成的有限序列(与链表类似)
数据类型:一组性质相同的值的集合及定义在此集合上的一些操作的总称,如整型、浮点型等
抽象数据类型(Abstract Data Type):对已有的数据类型进行抽象
ADT 抽象数据类型名
Data
数据元素之间逻辑关系的定义
Operation
操作
endADT
Operation:
InitList(*L):创建一个线性表
ListEmpty(L):判断线性表是否为空,返回一个布尔型变量
ClearList(*L):清空一个线性表
GetElem(L, i, *e):得到线性表L中第i个位置的值返回给e
LocateElem(L, e):找到线性表L中e的值所在的位置,成功返回元素在表中的序号,失败返回0
ListInsert(*L, i, e):将线性表L中第i个位置插入新元素e
ListDelete(*L, i, *e):删除线性表L中第i个位置元素,并用e返回其值
ListLength(L):返回线性表L的个数
用线性表实现A U B操作
#include <stdio.h>
/*
int main(void)
{
int n, i;
n = ListLength(A);
for(i=1 ; i<=n ; i++)
{
int x = 0;
x = GetElem(A, i);
if(LocateElem(B, x) == 0)
{
ListInsert(C, n+i, x);
}
}
}
*/
void unionL(List *La, List Lb)
{
int La_len, Lb_len, i;
ElemType e;
La_len = Listlength(*La);
Lb_len = Listlength(Lb);
for(i=1 ; i<=Lb_len ; i++)
{
GetElem(Lb, i, &e);
if(!LocateElem(*La, e))
{
ListInsert(La, ++La_len, e);
}
}
}
线性表的两种存储结构:顺序存储结构和链式存储结构
线性表的顺序存储结构的封装:
#define MAXSIZE 20
typedef int ElemType
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
顺序存储结构封装需要三个属性
1.存储空间的起始位置
2.线性表的最大存储容量
3.线性表的当前长度
获得元素操作GetElem:
typedef int Status;
Status GetElem(SqList L, int i, ELemType *e)
{
if(L.length == 0 || i<1 || i>L.length)
{
return 0;
}
*e = L.data[i-1];
return 1;
}
插入操作ListInsert:
#include "ListInsert.c.h"
#define MAXSIZE 20
typedef int Status
/*
Status ListInsert(Sqlist *L, int i, ElemType e)
{
int j, temp;
if(L.length == 0)
{
return 0;
}
L.length+=1;
for(j=i; i<=L.length; i++)
{
L.data[i-1] = temp;
L.data[i-1] = e;
e = temp;
}
}
*/
typedef int ElemType
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
Status ListInsert(Sqlist *L, int i, ElemType e)
{
int k;
if((*)L.length == MAXSIZE)
{
return 0;
}
if(i < 1 || i > (*)L.length+1)
{
return 0;
}
if(i <= (*)L.length)
{
for(k = (*)L.length-1; k >= i-1; k--)
{
(*)L.data[k+1] = (*)L.data[k];
}
}
(*)L.data[i-1] = e;
(*)L.length++;
return 1;
}
删除操作ListDelete:
#include "ListDelete.h"
#define MAXSIZE 20
typedef int Status
typedef int ElemType
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
Status ListDelete(Sqlist *L, int i, ElemType *e)
{
int k;
if((*)L.length == 0)
{
return 0;
}
if(i < 1 || i > (*)L.length)
{
return 0;
}
*e = (*)L.data[i-1];
if(i < (*)L.length)
{
for(k = i-1; k < (*)L.length-1; k++)
{
(*)L.data[k] = (*)L.data[k+1];
}
}
(*)L.length--;
return 1;
}
线性表的数据结构,在存、读数据时,不管是哪个位置,时间复杂度都是O(1),而在插入式删除时,时间复杂度都是O(n)
这就说明,它比较适合元素个数比较稳定,不经常插入和删除元素,而更多的操作是存取数据的应用
线性表顺序结构的优缺点:
优点:
1.无需为表示元素之间的逻辑关系而增加额外的存储空间
2.可以快速地存取表中任意位置的元素
缺点:
1.插入和删除操作需要移动大量元素
2.当线性表的长度变化较大时,难以确定存储空间的容量
3.容易造成存储空间的“碎片”
线性表的链式存储结构
头指针和头结点的区别
头指针:
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头节点的指针
头指针有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)
无论链表是否为空,头指针都不为空
头指针是链表的必要元素
头结点:
头结点是为了操作的统一和方便设立的,放在第一个元素的节点之前,其数据域一般无意义(也可以用来存放链表的长度)
有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其他结点的操作就统一了
头结点不一定是链表的必须要素
用C语言中可以用结构指针来描述单链表
typedef struct Node
{
ElemType data;
struct Node * Next;
}Node;
typedef struct Node *LinkList;
单链表的读取:
算法思路:
1.声明一个结点p指向链表第一个结点,初始化j从1开始
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j+1
3.若到链表末尾p为空,则说明第i个元素不存在
4.否则查找成功,返回结点p的数据
#include <stdio.h>
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
/*
Status GetElem(LinkList L, int i, ElemType *e)
{
ElemType j;
LinkList p;
p = L->next;
if(p = NULL)
{
return 0;
}
for(j=1 ; j<i ; j++)
{
p = p->next;
}
if(p->next == NULL)
{
return 0;
}
*e = p->data;
return 1;
}
*/
Status GetElem(LinkList L, int i, ElemType *e)
{
ElemType j;
LinkList p;
p = L->next;
j = 1;
while(p && j<i)
{
p = p->next;
++j;
}
if(!p || j>i)
{
return 0;
}
*e = p->data;
return 1;
}
单链表的插入:
算法思路:
1.声明一结点p指向链表头结点,初始化j从1开始
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
3.若到链表末尾p为空,则说明第i个元素不存在
4.否则查找成功,在系统中生成一个空结点s
5.将数据元素e赋值给s->data
6.单链表的插入刚才两个标准语句
#include <stdio.h>
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
Status ListInsert(LinkList L, int i, ElemType e)
{
ElemType j;
LinkList p, s;
p = *L; //equals to "p = L->next;"
j = 1;
while(p && j<i)
{
p = p->next;
j++;
}
if(!p || j>i)
{
return 0;
}
s = (LinkList)malloc(sizeof(Node)); //创建一个结点,在此之前s只是一个指向该结构体的指针
s->data = e;
s->next = p->next;
p->next = s;
return 0;
}
单链表的删除操作:
算法思路:
1.声明一结点p指向链表头结点,初始化j从1开始
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
3.若到链表末尾p为空,则说明第i个元素不存在
4.否则查找成功,使p->next = q; p->next = q->next;
5.将要删除节点q中的数据赋值给e,作为返回
6.释放q结点
#include <stdio.h>
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
Status ListDelete(LinkList L, int i, ElemType *e)
{
ElemType j;
LinkList p, q;
p = *L; //equals to "p = L->next;"
j = 1;
while(p && j<i)
{
p = p->next;
j++;
}
if(!p || j>i)
{
return 0;
}
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return 0;
}
单链表的整表创建:
算法思路:
1.声明一结点p和计数器变量i
2.初始化一空链表L
3.让L的头结点的指针指向NULL,即建立一个带头结点的单链表
4.循环实现后继结点的赋值和插入
头插法建立单链表:
#include <stdio.h>
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
Status CreateListHead(LinkList *L, int n)
{
LinkList p;
int i, e;
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
for(i=1 ; i<=n ; i++)
{
p = (LinkList)malloc(sizeof(Node));
printf("please input value:");
scanf_s("%d", &e);
p->data = e;
p->next = (*L)->next;
(*L)->next = p;
}
}
尾插法建立单链表:
#include <stdio.h>
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
Status CreateListTail(LinkList *L, int n)
{
LinkList p, q;
int i, e;
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
q = *L;
for(i=1 ; i<=n ; i++)
{
p = (LinkList)malloc(sizeof(Node));
printf("please input value:");
scanf_s("%d", &e);
p->data = e;
q->next = p;
p->next = NULL; //该步骤可省略
q = p;
}
q->next = NULL;
}
单链表的整表删除:
Status ClearList(LinkList *L)
{
LinkList p, q;
p = (*L)->next;
while(p)
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL;
return 1;
}
单链表结构与顺序存储结构优缺点(存储分配方式、时间性能、空间性能)
存储分配方式:
单链表为链式存储结构,存储空间不连续
顺序存储结构存储空间连续
时间性能:
顺序存储表在查找上仅需查找下标即可
单链表需要从表头开始查找
在插入和删除一个数据时,顺序存储表O(n),单链表O(1)
空间性能:
顺序存储表需要预分配空间,分配完成后空间确定
单链表无需分配空间
静态链表:
线性表的静态链表存储结构:(约定数组第一个元素和最后一个元素不存放值,第一个数组元素的游标存放除头尾外第一个没有数值的元素下标,最后一个数组元素的游标存放除头尾外第一个有数值的元素下标,最后一个有数值的数组元素游标存放第一个数组元素下标,此外其他元素的游标均存放下一个元素的下标)
#define MAXSIZE 1000
typedef struct
{
ElemType data; //数据
int cur; //游标
}Component, StaticLinkList[MAXSIZE];
静态链表的插入操作:
#include <stdio.h>
#define MAXSIZE 1000
typedef int ElemType;
typedef int Status;
typedef struct
{
ElemType data;
int cur;
}Component, StaticLinkList[MAXSIZE];
/*在静态链表L中第i个元素之前插入新的数据元素e*/
Status StaticListInsert(StaticLinkList L, int i, ElemType e)
{
int j, k, l;
k = MAXSIZE - 1;
if( i<1 || i>ListLength(L)+1 )
{
return 0;
}
j = Malloc_SLL(L);
if( j )
{
L[j].data = e;
for( l=1; l<=i+1; l++)
{
k = L[k].cur;
}
L[j].cur = L[k].cur;
L[k].cur = j;
return 1;
}
return 0;
}
/*获取空闲分量的下标*/
int Malloc_SLL(StaticLinkList space)
{
int i = space[0].cur;
if( space[0].cur )
{
space[0].cur = space[i].cur; //把它的下一个分量用来作为备用
return i;
}
}
静态链表的删除和获取数据个数操作:
#include <stdio.h>
#define MAXSIZE 1000
typedef int ElemType;
typedef int Status;
typedef struct
{
ElemType data;
int cur;
}Component, StaticLinkList[MAXSIZE];
/*删除静态链表L中第i个元素中的数据*/
Status StaticListDelete(StaticLinkList L, int i, ElemType *e)
{
int j, k, temp;
if( i<1 || i>ListLength(L)+1 )
{
return 0;
}
k = MAXSIZE - 1;
*e = L[i].data;
for( j=1 ; j<i ; j++)
{
k = L[k].cur;
}
j = L[k].cur;
L[k].cur = L[j].cur;
Free_SLL(L, j);
/*
temp = L[0].cur;
L[0].cur = i;
L[k].cur = L[i].cur;
L[i].cur = temp;
*/
}
/*将下标为k的空闲结点回收到备用链表*/
void Free_SLL(StaticLinkList space, int k)
{
space[k].cur = space[0].cur;
space[0].cur = k;
}
/*返回L中数据元素个数*/
Status ListLength(StaticLinkList L)
{
int n = 0;
int k = MAXSIZE - 1;
while( L[k].cur )
{
k = L[k].cur;
n++;
}
return n;
}
静态链表优缺点总结:
优点:在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点
缺点:没有解决连续存储分配(数组)带来的表长难以确定的问题,失去了顺序存储结构随机存取的特性
如何快速找到未知长度单链表的中间结点
普通方法:遍历一次半,算法复杂度:O(3L/2)
快慢指针法(标尺思想):设置两个指针,一个指针的移动速度是另一个指针的两倍,一个遍历完毕时,一个恰好在中间
代码实现:
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while( search->next != NULL )
{
if( search->next->next != NULL )
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->data;
rerurn 1;
}
写一个完整的程序,实现随机生成20个元素的链表,用快慢指针法查找中间的值并显示
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
void CreateListTail(LinkList *L, int n);
void PrintList(LinkList L);
void GetListLength(LinkList L);
Status GetMidNode(LinkList L, ElemType *e);
/*初始化顺序线性表*/
Status InitList(LinkList *L)
{
*L = (LinkList)malloc(sizeof(Node));
if(!(*L))
{
return 0;
}
(*L)->next = NULL;
return 1;
}
/*尾插法创建有20个随机数据的单链表*/
void CreateListTail(LinkList *L, int n)
{
LinkList p, q;
int i;
*L = (LinkList)malloc(sizeof(Node));
q = *L;
srand( time(0));
for( i=1 ; i<=n ; i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100+1;
q->next = p;
q = p;
}
q->next = NULL;
}
/*打印链表*/
void PrintList(LinkList L)
{
LinkList p;
p = L->next;
printf("Here are values of the list:\n");
while( p )
{
printf(" %d ", p->data);
p = p->next;
}
printf("\n");
}
/*得到链表的长度*/
void GetListLength(LinkList L)
{
LinkList p;
int n = 0;
p = L->next;
while( p )
{
n++;
p = p->next;
}
printf("The length of the list is %d\n", n);
}
/*快慢指针法得到中间的值*/
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while( search->next != NULL )
{
if( search->next->next != NULL )
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->data;
return 1;
}
int main(void)
{
char x;
Status i;
ElemType e;
LinkList L;
//i = InitList(&L);
printf("Welcome to the list factory!\n");
printf("1.CreateList 2.PrintList 3.GetListLength 4.GetMidListData 0.Exit\n");
while( x != '0' )
{
printf("Please enter the number to make appropriate choice:\n");
scanf("%c", &x);
getchar();
if( x == '1' )
{
int n;
n = MAXSIZE;
CreateListTail(&L, n);
}
else if( x == '2' )
{
PrintList(L);
}
else if( x == '3' )
{
GetListLength(L);
}
else if( x == '4' )
{
GetMidNode(L, &e);
printf("The mid data of the list is %d\n", e);
}
else if( x == '0' )
{
exit(0);
}
}
}
循环链表
单循环链表:将单链表终端结点的空指针改为指向头结点
循环链表不一定要有头结点
判断链表为空:head->next == head
循环链表具体实现代码:
/*单循环链表*/
/*单循环链表*/
#include <stdio.h>
#include <stdlib.h>
typedef struct CLinkList
{
int data;
struct CLinkList *next;
}node;
void ds_init(node **pNode);
void ds_insert(node **pNode, int i);
void ds_delete(node **pNode, int i);
void ds_search(node *pNode);
void ds_display(node *pNode);
int main(void)
{
node *Node = NULL;
int i = 1;
int j;
printf("Circular linked list\n");
printf("1.Init list 2.Display list 3.Insert value 4.Delete value 5.Search list 0.exit\n");
while( i )
{
printf("\nPlease enter the number to choose the operation\n");
scanf("%d", &i);
fflush(stdin);
switch( i )
{
case 1:
ds_init(&Node);
ds_display(Node);
break;
case 2:
ds_display(Node);
break;
case 3:
printf("Please enter the serial number:\n");
scanf("%d", &j);
ds_insert(&Node, j);
ds_display(Node);
break;
case 4:
printf("Please enter the serial number:\n");
scanf("%d", &j);
ds_delete(&Node, j);
ds_display(Node);
break;
case 5:
ds_search(Node);
break;
default:
exit(0);
}
}
}
/*初始化循环链表*/
void ds_init(node **pNode)
{
int item;
node *temp;
node *target;
printf("input the value of the node, and input 0 to complete init\n");
while(1)
{
printf("please input the value:\n");
scanf("%d", &item);
fflush(stdin);
if(item == 0)
{
return;
}
if((*pNode) == NULL)
{ /*循环链表中只有一个结点*/
*pNode = (node*)malloc(sizeof(struct CLinkList));
if(!(*pNode))
{
exit(0);
}
(*pNode)->data = item;
(*pNode)->next = *pNode;
}
else
{
/*找到next指向第一个结点的结点*/
for(target = (*pNode); target->next != (*pNode); target = target->next)
;
/*生成一个新的结点*/
temp = (node *)malloc(sizeof(struct CLinkList));
if(!temp)
{
exit(0);
}
temp->data = item;
temp->next = *pNode;
target->next = temp;
}
}
}
/*循环链表插入操作*/
void ds_insert(node **pNode, int i)
{
node *temp;
node *target;
int item;
int j = 1;
printf("please input the value:\n");
scanf("%d", &item);
fflush(stdin);
if(i == 1)
{
temp = (node *)malloc(sizeof(struct CLinkList));
if(!temp)
{
exit(0);
}
for(target = (*pNode); target->next != (*pNode); target = target->next) //因为是循环链表,所以插入结点是第一个时,必须先找到最后一个结点
;
temp->data = item;
temp->next = (*pNode);
target->next = temp;
*pNode = temp;
}
else
{
target = *pNode;
for( ; j<(i-1) ; ++j) //必须是i-1,因为target必须是i的上一个结点
{
target = target->next;
}
temp = (node *)malloc(sizeof(struct CLinkList));
if(!temp)
{
exit(0);
}
temp->data = item;
temp->next = target->next;
target->next = temp;
}
}
/*循环链表删除操作*/
void ds_delete(node **pNode, int i)
{
node *temp;
node *target;
int j = 1;
if( i == 1 )
{
for(target = (*pNode); target->next != (*pNode); target = target->next)
;
temp = *pNode;
target->next = (*pNode)->next;
*pNode = target->next;
free(temp);
}
else
{
target = *pNode;
for( ; j<(i-1) ; ++j)
{
target = target->next;
}
temp = target->next;
target->next = temp->next;
free(temp);
}
}
void ds_search(node *pNode)
{
node *target;
target = pNode;
int i = 3, j = 1;
int n;
printf("1.Enter the serial number to query the value 2.Enter the value query the serial number\n");
while( i < 1 || i > 2)
{
printf("Please enter the number:\n");
scanf("%d", &i);
fflush(stdin);
if( i == 1 )
{
printf("Please enter the serial number:\n");
scanf("%d", &n);
for( ; target->next != pNode; ++j)
{
if( n == j )
{
printf("The queried value is %d\n", target->data);
return 0;
}
else
{
target = target->next;
}
}
printf("The serial number is illegal!\n");
return 0;
}
else if( i == 2 )
{
printf("Please enter the value:\n");
scanf("%d", &n);
for( ; target->next != pNode; ++j)
{
if( n == target->data )
{
printf("The queried serial number is %d\n", j);
return;
}
else
{
target = target->next;
}
}
printf("The value dose not exist!\n");
return 0;
}
}
}
#if(0)
void ds_display(node *pNode)
{
node *temp;
temp = pNode;
printf("***********链表中的元素******************\n");
do
{
printf("%4d ", temp->data);
}while((temp = temp->next) != pNode);
printf("\n");
}
#endif
#if(1)
void ds_display(node *pNode)
{
node *target;
target = pNode;
printf("The Elements in the circular list:\n");
for( ; target->next != pNode ; target = target->next) //切记:不可在for语句后面加:
{
printf(" %d ", target->data);
}
printf(" %d ", target->data);
printf("\n");
}
#endif
约瑟夫问题:
/*约瑟夫问题进阶*/
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int num;
int data;
struct Node *next;
}Node;
void ds_create(Node **pNode, int n);
void ds_PwordTraverse(Node *pNode, int n);
void ds_Traverse(Node *pNode);
int main(void)
{
Node *pNode = NULL;
int n;
int i = 1;
printf("This is a solution of Josephus' problem\n");
printf("\n1.Init List 2.Traverse List 3.PwordTraverse List 4.Exit\n");
while(i<5 && i>0)
{
printf("Please enter the number to get the operation:\n");
scanf("%d", &i);
fflush(stdin);
switch(i)
{
case 1:
printf("\nPlease enter the length of the circular linked list:\n");
scanf("%d", &n);
ds_create(&pNode, n);
ds_Traverse(pNode);
break;
case 2:
ds_Traverse(pNode);
break;
case 3:
ds_PwordTraverse(pNode, n);
break;
case 4:
exit(0);
}
}
}
/*创建单循环链表*/
void ds_create(Node **pNode, int n)
{
Node *temp, *target;
int item, i = 1;
while(i <= n)
{
printf("Please enter the password of the number %d:\n", i);
scanf("%d", &item);
fflush(stdin);
if((*pNode) == NULL)
{
*pNode = (Node*)malloc(sizeof(Node));
if(!(*pNode))
{
exit(0);
}
(*pNode)->data = item;
(*pNode)->num = 1;
(*pNode)->next = *pNode;
}
else
{
for(target = (*pNode); target->next != (*pNode); target=target->next)
;
temp = (Node*)malloc(sizeof(Node));
if(!temp)
{
exit(0);
}
temp->data = item;
temp->num = i;
target->next = temp;
temp->next = *pNode;
}
i++;
}
}
/*根据密码遍历链表*/
void ds_PwordTraverse(Node *pNode, int n)
{
int i, pword;
int j = 0;
Node *temp, *target;
target = pNode;
do
{
pword = target->data;
for(i=1 ; i<pword ; i++)
{
target = target->next;
}
temp = target;
if( j<n-1 )
{
printf("%d->", temp->num);
}
else
{
printf("%d\n", temp->num);
printf("\n***********************\n");
}
free(temp);
j++;
}while( j<n );
}
/*根据链表顺序遍历链表并显示元素密码*/
void ds_Traverse(Node *pNode)
{
Node *target;
for( target = pNode ; target->next != pNode ; target = target->next )
{
printf("%d.[%d]->", target->num, target->data);
}
printf("%d.[%d]\n", target->num, target->data);
printf("\n***********************\n");
}
循环链表特点:
在单链表中,有了头结点时,访问第一个结点可以用O(1)的时间,但访问最后一个结点需要遍历完整个链表,需要O(n)的时间
在循环链表中,仅用O(1)的时间就可以访问到第一个和最后一个结点,有指向最后一个结点的尾指针即可
再上诉条件下,判断链表为空的条件:(假设尾指针为rear) rear=rear->next
/*合并两个单循环链表(仅关键语句)*/
Node *p1Node;
int *rear1, *rear2;
rear1->next = rear2->next->next;
free(rear2->next);
rear2->next = p1Node;
判断单链表中是否有环
方法一、使用p,q两个指针,p总是向前走(但是加上之前的步数),q每次从头开始走,当寻找一个确定结点时p,q的结果不一致时,存在环(双重循环实现)
方法二、快慢指针,p走一步,q走两步,若出现两者相等,存在环(单循环即可实现)
有趣的小例子:
1.魔术师发牌问题
/*魔术师发牌问题*/
#include <stdio.h>
#include <stdlib.h>
typedef struct Card
{
int data;
struct Card *next;
}Card;
void InitList(Card **pCard, int n);
void TraversingList(Card *pCard);
void DeleteList(Card *pCard);
void LicensingList(Card *pCard, int n);
int main(void)
{
Card *pCard = NULL;
int i, n = 0;
printf("The magician licensing problem\n");
printf("\n1.InitList 2.TraverseList 3.DeleteList 4.LicensingList 5.Exit\n");
while(1)
{
printf("Please enter the number to switch the operation:\n");
scanf("%d", &i);
fflush(stdin);
switch(i)
{
case 1:
printf("\nPlease enter the number of length:\n");
scanf("%d", &n);
InitList(&pCard, n);
TraversingList(pCard);
break;
case 2:
TraversingList(pCard);
break;
case 3:
DeleteList(pCard);
TraversingList(pCard);
break;
case 4:
if( n == 0)
{
printf("Please InitList first!\n");
printf("\n******************************\n");
break;
}
else
{
LicensingList(pCard, n);
TraversingList(pCard);
break;
}
case 5:
exit(0);
}
}
}
/*初始化循环链表*/
void InitList(Card **pCard, int n)
{
Card *temp, *target;
int i = 1;
while(i <= n)
{
if((*pCard) == NULL)
{
*pCard = (Card*)malloc(sizeof(struct Card));
if(!(*pCard))
{
exit(0);
}
(*pCard)->data = 0;
(*pCard)->next = *pCard;
}
else
{
temp = (Card*)malloc(sizeof(struct Card));
if(!temp)
{
exit(0);
}
for(target = *pCard ; target->next != *pCard ; target = target->next)
;
temp->data = 0;
target->next = temp;
temp->next = *pCard;
}
i++;
}
}
/*遍历循环链表*/
void TraversingList(Card *pCard)
{
Card *target;
for(target = pCard ; target->next != pCard ; target = target->next)
{
printf("%d->", target->data);
}
printf("%d\n", target->data);
printf("\n******************************\n");
}
/*删除链表*/
void DeleteList(Card *pCard)
{
Card *pHead, *pNext, *target;
pHead = pCard;
for(target = pCard ; target->next != pCard ; target = target->next)
;
while(pHead->next != pHead)
{
pNext = pHead->next;
target->next = pNext;
free(pHead);
pHead = pNext;
}
}
/*魔术师发牌推算*/
void LicensingList(Card *pCard, int n)
{
Card *target;
int i, count = 2;
target = pCard;
target->data = 1;
while(1)
{
for(i=0 ; i<count ; i++)
{
target = target->next;
if(target->data != 0) //该位置有牌的话,移向下一个位置
{
i--;
}
}
if(target->data == 0)
{
target->data = count;
count++;
if(count > n)
{
break;
}
}
}
}
2.拉丁方阵问题
/*拉丁方阵*/
#include <stdio.h>
#include <stdlib.h>
typedef struct Square
{
int data;
struct Square *next;
}Square;
void ds_InitList(Square **pSquare, int n);
void ds_TraverseList(Square *pSquare);
void ds_LatinSquare(Square *pSquare, int n);
void ds_DeleteList(Square *pSquare);
int main(void)
{
Square *pSquare = NULL;
int n;
int i = 1;
printf("The latin square:\n");
printf("\n1.Init List 2.Traverse List 3.LatinSquare List 4.Delete List 5.Exit\n");
while(1)
{
printf("Please enter the number to get the operation:\n");
scanf("%d", &i);
fflush(stdin);
switch(i)
{
case 1:
printf("\nPlease enter the length of the circular linked list:\n");
scanf("%d", &n);
ds_InitList(&pSquare, n);
ds_TraverseList(pSquare);
break;
case 2:
ds_TraverseList(pSquare);
break;
case 3:
ds_LatinSquare(pSquare, n);
break;
case 4:
ds_DeleteList(pSquare);
ds_TraverseList(pSquare);
break;
case 5:
exit(0);
}
}
}
/*初始化循环链表*/
void ds_InitList(Square **pSquare, int n)
{
Square *temp, *target;
int i = 1;
while(i <= n)
{
if((*pSquare) == NULL)
{
*pSquare = (Square*)malloc(sizeof(struct Square));
if(!(*pSquare))
{
exit(0);
}
(*pSquare)->data = i;
(*pSquare)->next = *pSquare;
}
else
{
for(target = *pSquare ; target->next != (*pSquare) ; target = target->next)
;
temp = (Square*)malloc(sizeof(struct Square));
if(!temp)
{
exit(0);
}
temp->data = i;
target->next = temp;
temp->next = *pSquare;
}
i++;
}
}
/*遍历循环链表*/
void ds_TraverseList(Square *pSquare)
{
Square *target;
printf("\nThe circular linked list:\n");
for(target = pSquare ; target->next != pSquare ; target = target->next)
{
printf("%d->", target->data);
}
printf("%d\n", target->data);
printf("\n*****************************\n");
}
/*输出拉丁方阵*/
void ds_LatinSquare(Square *pSquare, int n)
{
Square *target;
int i, j;
for(i=1 ; i<=n ; i++)
{
target = pSquare;
for(j=1 ; j<i ; j++)
{
target = target->next;
}
for(j=1 ; j<=n ; j++)
{
printf(" %d ", target->data);
target = target->next;
}
printf("\n");
}
printf("\n*****************************\n");
}
/*删除循环链表*/
void ds_DeleteList(Square *pSquare)
{
Square *pHead, *pNext, *target;
pHead = pSquare;
for(target = pSquare ; target->next != pSquare ; target = target->next)
;
while(pHead->next != pHead)
{
pNext = pHead->next;
target->next = pNext;
free(pHead);
pHead = pNext;
}
free(pHead);
}
双向链表:
双向链表结点结构:
typedef struct DualNode
{
ElemType data;
struct DualNode *prior; //前驱结点
struct DualNode *next; //后继节点
}DualNode, *DuLinkList;
双向链表插入操作:
/*假设s为要插入的结点,p为s的后继结点*/
s->next = p;
s->prior = p->prior;
p->prior->next = s;
p->prior = s;
双向链表删除操作:
/*假设要删除的结点为p*/
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
双向链表实例:
/*输入数字改变链表的输出顺序*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct Node
{
char data;
struct Node *prior;
struct Node *next;
}Node;
void InitDouList(Node **pNode, int n);
void TraverseList(Node *pNode);
void TraverNumberList(Node *pNode, int n);
int main()
{
Node *pNode = NULL;
int x, n;
printf("This is a program for the chaser's problem\n");
printf("\n****************************\n");
printf("\n1.InitList 2.TraveseList 3.ChaserList 4.Exit\n");
while(1)
{
printf("Please enter the number to switch the operation:\n");
scanf("%d", &x);
fflush(stdin);
switch(x)
{
case 1:
printf("\nPlease enter the number of length:\n");
scanf("%d", &n);
InitDouList(&pNode, n);
TraverseList(pNode);
break;
case 2:
TraverseList(pNode);
break;
case 3:
TraverNumberList(pNode, n);
break;
case 4:
exit(0);
}
}
return 0;
}
/*创建双循环链表*/
void InitDouList(Node **pNode, int n)
{
Node *target, *temp;
int i = 1;
char c = 'A';
while(i <= n)
{
if((*pNode) == NULL)
{
*pNode = (Node*)malloc(sizeof(struct Node));
if(!(*pNode))
{
exit(0);
}
(*pNode)->data = c;
(*pNode)->prior = *pNode;
(*pNode)->next = *pNode;
}
else
{
for(target = (*pNode) ; target->next != (*pNode) ; target = target->next)
;
temp = (Node*)malloc(sizeof(struct Node));
if(!temp)
{
exit(0);
}
temp->data = c;
target->next = temp;
temp->prior = target;
temp->next = *pNode;
(*pNode)->prior = temp;
}
c++;
i++;
}
}
/*遍历链表*/
void TraverseList(Node *pNode)
{
Node *target;
for(target = pNode ; target->next != pNode ; target = target->next)
{
printf("%c", target->data);
}
printf("%c", target->data);
printf("\n\n****************************\n");
}
/*根据输入的数字遍历链表*/
void TraverNumberList(Node *pNode, int n)
{
Node *target;
int i, j;
printf("\nPlease enter the number:\n");
scanf("%d", &i);
target = pNode;
if( i>=0 )
{
for(j=0 ; j<i ; j++)
{
target = target->next;
}
for(j=0 ; j<n ; j++)
{
if(j != (n-1))
{
printf("%c", target->data);
}
else
{
printf("%c\n", target->data);
printf("\n****************************\n");
}
target = target->next;
}
}
else
{
i = abs(i);
for(j=0 ; j<i ; j++)
{
target = target->prior;
}
for(j=0 ; j<n ; j++)
{
if(j != (n-1))
{
printf("%c", target->data);
}
else
{
printf("%c\n", target->data);
printf("\n****************************\n");
}
target = target->next;
}
}
}
/*删除链表*/
维吉尼亚加密(代码加密及解密部分出现问题):
/*维吉尼亚加密*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define OK 1
#define ERROR 0
typedef char ElemType;
typedef int Status;
typedef struct Letter
{
ElemType data;
struct Letter *prior;
struct Letter *next;
}Letter;
typedef struct Vigen
{
ElemType rdata;
int key;
ElemType edata;
struct Vigen *next;
}Vigen;
Status InitList(Vigen **pVigen);
Status ds_InitList(Letter **pLetter);
void ds_TraverseList(Letter *pLetter);
void TraverseList(Vigen *pVigen);
Status ds_EncryptList(Letter *pLetter, Vigen *pVigen);
Status ds_DecryptList(Letter *pLetter, Vigen *pVigen);
Status SaveList(Letter *pLetter, Vigen *pVigen);
int main()
{
Letter *pLetter = NULL;
Vigen *pVigen = NULL;
int x, i;
printf("This is a Vigenere's encrypted algorithm\n");
printf("\n1.InitList of letters 2.InitList of data 3.TraverseList of letters 4.TraverseList of data\n");
printf("\n5.EncryptList of data 6.DecryptList of data 7.SaveList 8.Exit\n");
while(1)
{
printf("\nPlease enter the number to choose the operation\n");
scanf("%d", &x);
fflush(stdin);
switch(x)
{
case 1:
i = ds_InitList(&pLetter);
if(i == 1)
{
printf("Operation success!\n");
ds_TraverseList(pLetter);
}
else
{
printf("Operation failed!\n");
}
break;
case 2:
i = InitList(&pVigen);
if(i == 1)
{
printf("Operation success!\n");
TraverseList(pVigen);
}
else
{
printf("Operation failed!\n");
}
break;
case 3:
ds_TraverseList(pLetter);
break;
case 4:
TraverseList(pVigen);
break;
case 5:
i = ds_EncryptList(pLetter, pVigen);
if(i == 1)
{
printf("Operation success!\n");
TraverseList(pVigen);
}
else
{
printf("Operation failed!\n");
}
break;
case 6:
i = ds_DecryptList(pLetter, pVigen);
if(i == 1)
{
printf("Operation success!\n");
TraverseList(pVigen);
}
else
{
printf("Operation failed!\n");
}
break;
case 7:
i = SaveList(pLetter, pVigen);
if(i == 1)
{
printf("Operation success!\n");
}
else
{
printf("Operation failed!\n");
}
break;
case 8:
exit(0);
}
}
return 0;
}
/*创建数据链表*/
Status InitList(Vigen **pVigen)
{
Vigen *target, *temp;
int flag;
ElemType item;
srand((int)time(0));
printf("Please enter 1 or 0 to choose encryption or decryption:\n");
scanf("%d", &flag);
fflush(stdin);
while(1)
{
printf("\nPlease enter the data of number:\n");
scanf("%c", &item);
fflush(stdin);
if(item == '0')
{
break;
}
if((*pVigen) == NULL)
{
*pVigen = (Vigen*)malloc(sizeof(struct Vigen));
if(!(*pVigen))
{
return ERROR;
}
if(flag == 1)
{
(*pVigen)->rdata = item;
(*pVigen)->edata = '0';
}
else
{
(*pVigen)->rdata = '0';
(*pVigen)->edata = item;
}
(*pVigen)->key = 1+(int)(rand()%100 + 1);
(*pVigen)->next = *pVigen;
}
else
{
for(target = (*pVigen) ; target->next != (*pVigen) ; target = target->next)
;
temp = (Vigen*)malloc(sizeof(struct Vigen));
if(!temp)
{
return ERROR;
}
if(flag == 1)
{
temp->rdata = item;
temp->edata = '0';
}
else
{
temp->rdata = '0';
temp->edata = item;
}
temp->key = 1+(int)(rand()%100 + 1);
target->next = temp;
temp->next = *pVigen;
}
}
return OK;
}
/*创建字母本链表*/
Status ds_InitList(Letter **pLetter)
{
Letter *target, *temp;
int i = 0;
while(i < 26)
{
if((*pLetter) == NULL)
{
*pLetter = (Letter*)malloc(sizeof(struct Letter));
if(!(*pLetter))
{
return ERROR;
}
(*pLetter)->data = 'A'+i;
(*pLetter)->prior = *pLetter;
(*pLetter)->next = *pLetter;
}
else
{
for(target = (*pLetter) ; target->next != (*pLetter) ; target = target->next)
;
temp = (Letter*)malloc(sizeof(struct Letter));
if(!temp)
{
return ERROR;
}
temp->data = 'A'+i;
temp->prior = target;
target->next = temp;
(*pLetter)->prior = temp;
temp->next = *pLetter;
}
i++;
}
return OK;
}
/*遍历字母链表*/
void ds_TraverseList(Letter *pLetter)
{
Letter *target;
for(target = pLetter ; target->next != pLetter ; target = target->next)
{
printf("%c->", target->data);
}
printf("%c\n", target->data);
printf("\n********************************\n");
}
/*遍历数据链表*/
void TraverseList(Vigen *pVigen)
{
Vigen *target;
for(target = pVigen ; target->next != pVigen ; target = target->next)
{
printf("[%c,%d,%c]->", target->rdata, target->key, target->edata);
}
printf("[%c,%d,%c]\n", target->rdata, target->key, target->edata);
printf("\n********************************\n");
}
/*加密数据*/
Status ds_EncryptList(Letter *pLetter, Vigen *pVigen)
{
Vigen *p;
Letter *q;
int i, j;
for(p = pVigen ; p->next != pVigen ; p = p->next)
{
i = p->key;
if(i > 26) //随机数大于26取余数,简化后续循环运算
{
i = i%26;
}
for(q = pLetter ; q->next != pLetter ; q = q->next)
{
if(q->data == p->rdata)
{
for(j=1 ; j<i ; j++) //q向后移动i位,并将i位后的数据赋给p->edata
{
q = q->next;
}
p->edata = q->data;
}
}
if(q->data == p->rdata) //字母表的最后一个字母Z
{
for(j=1 ; j<i ; j++)
{
q = q->next;
}
p->edata = q->data;
}
else //如果都不相等,说明源码此处为空格
{
p->edata = p->rdata;
}
}
return OK;
}
/*解密数据*/
Status ds_DecryptList(Letter *pLetter, Vigen *pVigen)
{
Vigen *p;
Letter *q;
int i, j;
for(p = pVigen ; p->next != pVigen ; p = p->next)
{
i = p->key;
if(i > 26) //随机数大于26取余数,简化后续循环运算
{
i = i%26;
}
for(q = pLetter ; q->next != pLetter ; q = q->next)
{
if(q->data == p->edata)
{
for(j=1 ; j<i ; j++) //q向前移动i位,并将i位后的数据赋给p->rdata
{
q = q->prior;
}
p->rdata = q->data;
}
}
if(q->data == p->edata) //字母表的最后一个字母Z
{
for(j=1 ; j<i ; j++)
{
q = q->prior;
}
p->rdata = q->data;
}
else //如果都不相等,说明源码此处为空格
{
p->rdata = p->edata;
}
}
return OK;
}
/*保存字母表及数据表*/
Status SaveList(Letter *pLetter, Vigen *pVigen)
{
FILE *fp;
Letter *p;
Vigen *q;
if((fp = fopen("listdata.txt", "w")) == NULL)
{
printf("Fail to open the file!\n");
return ERROR;
}
for(p = pLetter ; p->next != pLetter ; p = p->next)
{
fwrite(p, sizeof(struct Letter), 1, fp);
}
fwrite(p, sizeof(struct Letter), 1, fp);
for(q = pVigen ; q->next != pVigen ; q = q->next)
{
fwrite(q, sizeof(struct Vigen), 1, fp);
}
fwrite(q, sizeof(struct Vigen), 1, fp);
fclose(fp);
return OK;
}
/*读取文件*/