在这里,我将展示用头插法的方法实现链表的就地逆转。原理是对于一个给定的链表,先将其头结点摘出来,接下来,依次将链表中的结点摘出来,用头插法插入到摘出来的头结点之后,直到将所有的结点都插入,那么就实现了链表的就地逆转。下面给的程序是c语言程序,可以在gcc里面运行出来。并且有详细的注释,因而在这儿就不再赘述类,看下面的程序。
注意:a.txt文件中的内容就是我们要逆转的数据,要跟我们的.c文件放在一起,否则就要将路径完整写出!!!
#include <stdio.h>
#include <stdlib.h>
//=====================================================
//操作状态
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
//=====================================================
// 操作状态类型
typedef int Status;
// 数据元素类型
typedef int ElemType;
//=====================================================
// 结点类型
typedef struct tag_LNode{
int data;
struct tag_LNode *next;
}LNode;
//=====================================================
// 链表类型(带头结点的单链表)
typedef struct {
int lenth; // 指示链表长度
LNode *head; // 分别指向头结点和最后一个结点的指针
LNode *current; // 指向当前被访问的结点的指针,初始位置指向头结点
} LinkList;
//=====================================================
//构造一个空的线性表L
Status InitList(LinkList *L)
{
L->head = (LNode *)malloc(sizeof(LNode));//开辟一个新的空间
if(!L->head) return ERROR; //空间开辟不成功
L->head->next = NULL; //初始化线性表L
L->lenth = 0;
L->current = L->head;
return OK;
}
//=====================================================
//销毁线性表L
void DestroyList(LinkList *L) //销毁线性表 L
{
LNode *p;
p = L->head; //p指向表头
while(p) //依次从表头开始删除每一个结点
{
L->head = L->head->next;//摘出p
free(p); //删除p
p = L->head; //又将p指向表头
}
}
//=====================================================
//清空线性表的元素
void ClearList(LinkList *L)
{
LNode *p;
while(L->head->next) //删除除了表头的所有结点
{
p = L->head->next; //p指向表头的下一个结点
L->head->next = p->next;//摘出结点p
free(p); //删除p
}
}
//=====================================================
// 改变当前指针指向第i个结点
Status LocatePos(LinkList *L, int i )
{
int k;
k = 0;
L->current = L->head; //初始化当前指针指向头指针处
while(L->current->next && k < i)//移动当前指针,直到指向第i个位置结束
{
L->current = L->current->next;
k++;
}
if(k != i) return ERROR;
return OK;
}
//=====================================================
//在当前位置之后插入数据元素e
Status InsertAfter(LinkList *L, ElemType e)
{
LNode *s;
if(!L->current) return ERROR;//当前指针为空,就返回错误
s = (LNode *)malloc(sizeof(LNode));//给s开辟新空间
if(!s) return ERROR; //s开辟空间失败
s->data = e; //将e赋给s->data
if(L->current->next == NULL)//如果当前指针是最后一个,那么直接插入到最后即可
L->current->next = s;
else //当前指针不在最后时,我们就在当前指针之后插入
{
s->next = L->current->next;
L->current->next = s;
}
L->lenth++; //插入一个结点后,表的长度要加1
return OK;
}
//=====================================================
//在表头插入数据元素e
Status InsertHead(LinkList *L, ElemType e)
{
LNode *p;
p = (LNode *)malloc(sizeof(LNode));//给p开辟新空间
if(!p) return ERROR; //p开辟空间失败
p->data = e; //将e赋给p->data
p->next = NULL;
p->next = L->head->next; //在表头插入结点p
L->head->next = p;
L->lenth++;
return OK;
}
//=====================================================
//输出单链表的数据
void PrintOut(LinkList L)
{
LNode *p;
printf("链表有%d个元素:",L.lenth);
p = L.head->next;
while(p) //当p非空时,输出p的数据值
{
printf("%d ", p->data);
p = p->next;
}
}
//=====================================================
//在表中查找数据元素e,让当前指针(current)指向该结点
Status LocateElem(LinkList *L, ElemType e)
{
LNode *p;
p = L->head->next;
while(p)
{
if(p->data == e) //依次判定该结点的数据是不是e
{
L->current = p; //将当前指针指向p
return OK;
}
p = p->next;
}
return ERROR;
}
//=====================================================
//求线性表的长度
int ListLenth(LinkList L)
{
return L.lenth; //直接返回线性表L的长度即可
}
//=====================================================
//线性表就地逆转(前插法)
void ListReverse(LinkList *L)
{
LNode *p,*q;
p = L->head->next; //p指向表头的下一个结点
L->head->next = NULL; //摘出表头
while(p) //依次用前插法插入结点
{
q = p->next; //q指向p的下一个结点,将p摘出来
p->next = L->head->next;//将摘出来的p出入到表头的后面
L->head->next = p;
p = q; //将p指向q指向的位置
}
}
//=====================================================
int main(int argc,char* argv)
{
FILE *fp; //文件指针
LinkList L; //线性表
ElemType e;
int j; //j作为后面选项的变量
int i = 1; //i作为while循环的判定变量
if((fp = fopen("a.txt", "r")) == 0)//打开文件“a.txt”,r表示可读的意思
{
printf("打开文件失败");
return -1;
}
if(!InitList(&L)) //初始化链表L
{
printf("初始化链表失败\n");
return -2;
}
LocatePos(&L, 0); //改变当前指针指向表的头结点
while(1) //读入数据并插入到表中
{
if(fscanf(fp, "%d", &e) == EOF)//文件中的数据读数据到单链表L,知道读到文件最后,再跳出while循环,EOF是end of file 的意思
break;
InsertAfter(&L, e);
}
while(i)
{
printf("1.查看原始链表中的数据\n");
printf("2.输出链表长度\n");
printf("3.查找链表中的元素\n");
printf("4.逆转该链表,并输出逆转后的数据\n");
printf("5.销毁链表\n");
printf("0.退出!\n");
printf("选择你要进行的操作\n");
scanf("%d", &j); //输入选项
printf("\n");
switch(j) //使用了switch-case来对选择不同的选项进行不同的操作
{
case 1: //输出单链表L的数据
printf("\n原始链表\n");
PrintOut(L); //输出原始链表中的数据
printf("\n\n");
break;
case 2: //链表长度
printf("链表长度:%d\n", ListLenth(L));//调用了链表长度的函数
printf("\n");
break;
case 3: //查找链表元素
printf("输入要查找的数: ");
scanf("%d", &e);
if(LocateElem(&L, e))//LocateElem(&L,e)是调用定位函数,将当前指针定位到元素e的位置
printf("元素%d在链表中\n", L.current->data);
else
printf("元素%d不在链表中\n", e);
printf("\n");
break;
case 4: //链表就地逆转
printf("\n原始链表\n");
PrintOut(L); //输出原始链表里面的元素
printf("\n\n");
ListReverse(&L);//调用逆转链表的函数
printf("\n逆转链表\n");
PrintOut(L);
printf("\n\n");
break;
case 5: //销毁链表L
DestroyList(&L);//调用销毁链表的函数
printf("\n");
break;
case 0:
printf("\n结束!\n");
i = 0; //i = 0使得跳出整个while循环,结束程序
printf("\n");
}
}
return 0;
}