头文件
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int ElemType;
typedef struct LNode //建立链表结构体
{
ElemType data;
struct LNode* next; //还未定义别名只能用struct LNode定义next指针
}LinkList;
//functions.cpp里面函数声明
void InitList(LinkList*& L); //初始化链表
void CreateList(LinkList*& L, ElemType a[], int n);//尾插法建表
int GetLength(LinkList*& L); //求链表长度
void PrintList(LinkList*& L); //输出链表中的元素
void ListEmpty(LinkList*& L); //判断链表是否为空
void PrintElem(LinkList*& L, int i, ElemType& e); //输出链表中的第i个元素
void LocateElem(LinkList*& L, ElemType e); //获取元素a的逻辑位置
bool InsertElem(LinkList*& L, int i, ElemType e);//在单链表的第i个位置插入值为e的结点
bool DeleteElem(LinkList*& L, int i, ElemType& e);//删除单链表中的第i个结点并用e返回其数据域值
void DestroyList(LinkList*& L);//销毁单链表
void Circulate(LinkList*& L);//使单链表变为循环单链表
void PrintCirList(LinkList*& L);//打印循环链表
void DestroyCirList(LinkList*& L);//销毁循环单链表
LinkList* createNode(ElemType data);//第一步:创建新结点
//在链表末尾添加结点
void appendNode(LinkList*&head, ElemType data); //调用的时候写的是appendNode(&head,data)
//#####test2-1里的函数声明
void InitCirList(LinkList*& L); //初始化循环单链表
LinkList* createCirNode(ElemType data);//第一步:创建新结点
//在循环单链表末尾添加结点
void appendCirNode(LinkList*&head, ElemType data); //调用的时候写的是appendNode(&head,data)
int GetCirLength(LinkList*& L); //求循环单链表长度
void PrintCirList(LinkList*& L);//打印循环链表
int CirIsEmpty(LinkList*& L);//判断循环单链表是否为空
void PrintCirElem(LinkList*& L, int i, ElemType& e); //输出循环单链表中的第i个元素
void LocateCirElem(LinkList*& L, ElemType e); //获取循环单链表中元素a的逻辑位置
bool InsertCirElem(LinkList*& L, int i, ElemType e);//在循环单链表的第i个位置插入值为e的结点
bool DeleteCirElem(LinkList*& L, int i, ElemType& e);//删除循环单链表中的第i个结点并用e返回其数据域值
void DestroyCirList(LinkList*& L);//销毁循环单链表
//test2-2里的函数声明
// 获取链表中间结点(对于偶数长度链表,返回第二个中间结点)
LinkList* findMiddleNode(LinkList* head);
//test2-3里的函数声明
functions.cpp
#include"Sq.h"
void InitList(LinkList*& L) //初始化链表
{
L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
L->next = NULL; //其next域置为空
}
void CreateList(LinkList*& L, ElemType a[], int n)//尾插法建表
{
LinkList* s, * r; //s是用于暂存的中间结点,而r是用于进行链接的结点,始终指向尾结点,但一开始时指向头结点
L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
r = L;
int i = 0;
for (i = 0; i < n; i++)
{
s = (LinkList*)malloc(sizeof(LinkList)); //创建新结点
s->data = a[i];
r->next = s; //将*s插入*r之后,即L之后
r = s; //移动尾指针
}
r->next = NULL; //尾结点next域置为NULL
}
LinkList* createNode(ElemType data)//第一步:创建新结点
{
LinkList* newNode = (LinkList*)malloc(sizeof(LinkList));
if (newNode == NULL)
{
printf("内存分配失败\n");
exit(1);
}
newNode->data = data; //赋值
newNode->next = NULL; //next域置空
return newNode;
}
//在链表末尾添加结点
void appendNode(LinkList*& head, ElemType data) //调用的时候写的是appendNode(&head,data)
{
LinkList* newNode = createNode(data);
if (head->next==NULL)
{
head->next = newNode;
return;
}
LinkList* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
}
int GetLength(LinkList*& L) //求链表长度
{
LinkList* p = L; //设置指针p向后扫描整个链表
int n = 0; //设置n为计数器,起始值为0
while (p->next != NULL)
{
n++;
p = p->next;
}
printf("链表的长度为%d", n);
return n;
}
void PrintList(LinkList*& L) //输出链表中的元素
{
LinkList* p = L;
printf("链表中的元素有:");
while (p->next != NULL)
{
printf("%d\t", p->next->data);
p = p->next;
}
printf("\n");
}
void ListEmpty(LinkList*& L) //判断链表是否为空
{
if (L->next == NULL)
{
printf("该链表为空\n");
}
else
{
printf("该链表不为空\n");
}
}
void PrintElem(LinkList*& L, int i, ElemType& e) //输出链表中的第i个元素
{
int j = 0;
LinkList* p = L;
while (p != NULL && j < i)
{
j++;
p = p->next;
}
if (p == NULL)
{
printf("sorry not found");
exit(0);
}
else
{
e = p->data;
}
printf("第%d个元素是%d\n", i, e);
}
void LocateElem(LinkList*& L, ElemType e) //获取元素a的逻辑位置
{
int i = 1, flag = 0;;
LinkList* p = L->next;
while (p != NULL)
{
if (p->data == e)
{
printf("%d的逻辑位置在第%d位\n", e, i);
flag = 1;
}
i++;
p = p->next;
}
if (flag == 0)
{
printf("sorry not found");
exit(0);
}
}
bool InsertElem(LinkList*& L, int i, ElemType e)//在单链表的第i个位置插入值为e的结点
{
LinkList* p = L, * s;
if (i < 1)
{
return false;
}
int j = 0;
while (p != NULL && j < i - 1)
{
j++;
p = p->next;
}
if (p == NULL)
{
printf("sorry not found");
}
else
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
}
bool DeleteElem(LinkList*& L, int i, ElemType& e)//删除单链表中的第i个结点并用e返回其数据域值
{
LinkList* p = L, * q;
if (i < 1)
{
return false;
}
int j = 0;
while (p != NULL && j < i - 1)
{
j++;
p = p->next;
}
if (p == NULL)
{
printf("sorry not found");
return false;
}
else
{
q = p->next;
if (q == NULL)
{
printf("sorry not found");
return false;
}
e = q->data;
p->next = q->next;
free(q);
return true;
}
}
void DestroyList(LinkList*& L)//销毁单链表
{
LinkList* pre = L, * p = L->next;
while (p != NULL)
{
free(pre);
pre = p;
p = p->next;
}
free(pre);//循环结束时,p为NULL,pre指向尾结点,释放它
}
void Circulate(LinkList*& L)//使单链表变为循环单链表
{
LinkList* p = L;
while (p->next != NULL)
{
p = p->next;
}
p->next = L;
}
编写一个程序,实现循环单链表的各种基本运算,本实验的单链表元素的类型为int,在主函数中调用循环单链表的基本操作完成如下操作:
(1)初始化循环单链表h
(2)依次插入1、3、1、4
(3)输出循环单链表L的长度
(4)输出循环单链表L
(5)判断循环单链表L是否为空
(6)输出循环单链表的第3个元素
(7)输出元素3的逻辑位置
(8)在第4个元素位置上插入元素5
(9)输出循环单链表L
(10)删除L的第3个元素
(11)输出单链表L
(12)释放单链表L
test2-1.cpp
#include"Sq.h"
void InitCirList(LinkList*& L) //初始化循环单链表
{
L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
L->next = L; //其next域置为头结点
}
LinkList* createCirNode(ElemType data)//第一步:创建新结点
{
LinkList* newNode = (LinkList*)malloc(sizeof(LinkList));
if (newNode == NULL)
{
printf("内存分配失败\n");
exit(1);
}
newNode->data = data; //赋值
newNode->next = NULL; //next域置空
return newNode;
}
//在循环单链表末尾添加结点
void appendCirNode(LinkList*&head, ElemType data) //调用的时候写的是appendNode(&head,data)
{
LinkList* newNode = createNode(data);
if (head->next==head)
{
head->next = newNode;
newNode->next = head;
}
else
{
LinkList* temp = new LinkList;
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = newNode;
newNode->next = head;
}
}
int GetCirLength(LinkList*& L) //求循环单链表长度
{
LinkList* p = L; //设置指针p向后扫描整个链表
int n = 0; //设置n为计数器,起始值为0
if ( L->next == L)//空链表或只有头结点的链表
{
return 0;
}
while (p->next != L)
{
n++;
p = p->next;
}
printf("链表的长度为%d\n", n);
return n;
}
void PrintCirList(LinkList*& L)//打印循环链表
{
LinkList* p = L;
printf("循环链表中的元素有:");
while (p->next != L)
{
printf("%d\t", p->next->data);
p = p->next;
}
printf("\n");
}
int CirIsEmpty(LinkList*& L)//判断循环单链表是否为空
{
if (L == NULL || L->next == L)
{
printf("链表为空\n");
return 1;
}
else
{
return 0;
}
}
void PrintCirElem(LinkList*& L, int i, ElemType& e) //输出循环单链表中的第i个元素
{
int j = 0;
LinkList* p = L->next;
while (p != L && j < i-1)//j小于i-1是因为一开始p就指向了第一个结点而不是头结点
{
j++;
p = p->next;
}
if (p == L)
{
printf("sorry not found");
exit(0);
}
else
{
e = p->data;
}
printf("第%d个元素是%d\n", i, e);
}
void LocateCirElem(LinkList*& L, ElemType e) //获取循环单链表中元素a的逻辑位置
{
int i = 1, flag = 0;;
LinkList* p = L->next;
while (p != L)
{
if (p->data == e)
{
printf("%d的逻辑位置在第%d位\n", e, i);
flag = 1;
}
i++;
p = p->next;
}
if (flag == 0)
{
printf("sorry not found\n");
exit(0);
}
}
bool InsertCirElem(LinkList*& L, int i, ElemType e)//在循环单链表的第i个位置插入值为e的结点
{
LinkList* p = L->next, * s;
if (i < 1||i>GetCirLength(L)+1)
{
return false;
}
int j = 1;
while (p != L && j < i - 1)
{
j++;
p = p->next;
}
if (p == L)
{
appendCirNode(L, e);
}
else
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
}
bool DeleteCirElem(LinkList*& L, int i, ElemType& e)//删除循环单链表中的第i个结点并用e返回其数据域值
{
LinkList* p = L->next, * q;
if (i < 1)
{
return false;
}
int j = 1;
while (p != L && j < i - 1)//p是要删除的结点的上一结点
{
j++;
p = p->next;
}
if (p == L)
{
printf("sorry not found\n");
return false;
}
else
{
q = p->next;
if (q == L)
{
printf("sorry not found\n");
return false;
}
e = q->data;
p->next = q->next;
free(q);
return true;
}
}
void DestroyCirList(LinkList*& L)//销毁循环单链表
{
LinkList* pre = L, * p = L->next;
while (p != L)
{
free(pre);
pre = p;
p = p->next;
}
free(pre);//循环结束时,p为NULL,pre指向尾结点,释放它
}
/*
int main(int argc, char* argv[])
{
LinkList* L=new LinkList;
InitCirList(L);
appendCirNode(L,1);
appendCirNode(L, 3);
appendCirNode(L, 1);
appendCirNode(L, 4);
GetCirLength(L);
PrintCirList(L);
int e=0;
PrintCirElem(L,3,e);
LocateCirElem(L,3);
InsertCirElem(L,4,5);
PrintCirList(L);
DeleteCirElem(L,3,e);
printf("被删除的元素是%d\n",e);
PrintCirList(L);
DestroyCirList(L);
return 0;
}
*/
已知带头结点的单链表L,设计一个算法求链表的中间结点。如果链表长度为奇数,返回唯一的中间结点,如果链表长度为偶数,返回第二个中间结点。
test2-2
#include"Sq.h"
// 获取链表中间结点(对于偶数长度链表,返回第二个中间结点)
LinkList* findMiddleNode(LinkList* head)
{
LinkList* slow = head->next;
LinkList* fast = head->next;
while (fast != NULL && fast->next != NULL)
//fast!=NULL这个条件是针对有奇数个结点时,比如5个结点,fast指针只能跳到了第5个结点的next域,即为NULL。
//fast->next!=NULL这个条件是针对于有偶数个结点时,比如6,fast刚好跳到了第6个结点,第6个结点不为空但它的next域为空
{
slow = slow->next;
fast = fast->next->next;
}
// 如果链表长度为偶数,移动慢指针一次
if (fast != NULL) //因为链表长度为奇数时fast==NULL
{
slow = slow->next;
}
return slow;
}
/*int main(int argc, char* argv[])
{
LinkList* L=new LinkList;
InitList(L);
appendNode(L,1);
appendNode(L, 2);
appendNode(L, 3);
appendNode(L, 4);
appendNode(L, 5);
appendNode(L, 6);
printf("原");
PrintList(L);
LinkList* middleNode = findMiddleNode(L);
printf("Middle node: %d\n", middleNode->data);
return 0;
}
*/
编写算法逆置一个带头结点的双链表
test2-3
#include"Sq.h"
typedef struct DNode//定义双链表的结构体
{
int data;
struct DNode* prev;
struct DNode* next;
}DNode;
DNode* createDNode(int data)//创建新结点
{
DNode* newNode = (DNode*)malloc(sizeof(DNode));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
void insertDNode(DNode*& head, int data)//在双链表末尾插入新结点
{
DNode* newNode = createDNode(data);
if (head->next == NULL)//如果头结点的next域为空则将新结点变为头结点next域,即第一个数据结点
{
head->next = newNode;
return;
}
else
{
DNode* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
newNode->prev = temp;
}
}
void PrintDList(DNode* head)//打印双链表
{
DNode* temp = head;
while (temp->next != NULL)//注意这里是temp而不是temp->next因为在目前这里的双链表定义中没有无数据的头结点
{
printf("%d\t", temp->next->data);
temp = temp->next;
}
printf("\n");
}
void reverseDList(DNode* head)
/**
* 采用的思想是先将第一个数据结点跟头结点断开,
* 把下一结点拼接到前部分结点的前面形成一个新的链表,
* 再依次将后面的结点拼接到这个链表的前面实现逆置
* eg.原链表是1、2、3、4,逐渐变为
* 2、1
* 3、2、1
* 4、3、2、1
* 再将头结点接回新链表的的前面
*/
{
if (head->next == NULL || head->next->next == NULL)
{
return; // 链表为空或只有一个节点,无需逆置
}
DNode* current = head->next;//令current为第一个数据结点
DNode* temp = NULL;
current->prev = NULL;//将第一个结点的prev域置为空,要将其与头结点先断开方便后面的逆置操作
temp = current->next;//令temp为第二个数据结点
//将第一个结点的next域置为空,因为逆置的话第一个结点要变为最后一个结点,next域要变为NULL才方便实现打印之类的操作
current->next = NULL;
DNode* temp2;
while (temp != NULL)
{
temp2 = temp->next;//temp2保存将要处理的结点的下一结点
//将temp接到current的前面
current->prev = temp;
temp->next = current;
//将current变为temp,即被接到前面的结点变为第一个
current = temp;
//将要处理的结点变为下一个
temp = temp2;
}
//将头结点接回到新链表的前面
current->prev = head;
head->next = current;
}
void DestroyDList(DNode* &head)//销毁双链表
{
DNode* current = head;
DNode* temp;
while (current != NULL)
{
temp = current;
current = current->next;
free(temp);
}
head = NULL;//将头结点指针置为空
}
/*int main(int argc, char* argv[])
{
DNode* head = new DNode;
head->prev = NULL;
head->next = NULL;
insertDNode(head, 1);
insertDNode(head, 2);
insertDNode(head, 3);
insertDNode(head, 4);
printf("原双链表为:");
PrintDList(head);
reverseDList(head);
printf("逆置后双链表为:");
PrintDList(head);
DestroyDList(head);
return 0;
}
*/
已知带头结点的单链表L,设计一个算法,以单链表的首结点值x为基准,将该单链表分割为两个部分,使所有小于x的结点排在大于或等于x的结点之前。例如,原链表为(3,5,4,1,2),完成该算法后链表为(2,1,3,5,4)。要求空间复杂度为O(1)。(可以用头插法或者交换)
test2-4
#include"Sq.h"
/*****************************************************************//**
* 已知带头结点的单链表L,设计一个算法,以单链表的首结点值x为基准,
* 将该单链表分割为两个部分,使所有小于x的结点排在大于或等于x的结点之前。
* 例如,原链表为(3,5,4,1,2),完成该算法后链表为(2,1,3,5,4)。
* 要求空间复杂度为O(1)。(可以用头插法或者交换)
*********************************************************************/
void DevideL(LinkList*& L, int x)//
{
if (L->next == NULL || L->next->next == NULL)
{ //如果为空链表或只有一个数据结点的话则结束
return;
}
//一开始用front来保存第一个数据结点,若有后面的数据插上来则front指向这个新插上来的数据结点
LinkList* front = L->next;
//设置一个rear指针指向开始往下轮着处理完的链表的尾部,比如3,5,4,1,2
// 处理完3和5,则rear指向5这个结点
LinkList* rear = L;//一定要注意一开始时rear是指向头结点的!
//temp1指针用来指向要处理的结点,一开始是第一个数据结点,一般在rear指针的后面
LinkList* temp1 = L->next;
LinkList* temp2;//temp2用于暂存temp1的下一个结点
while (temp1 != NULL)
{
if (temp1->data < x)
{
temp2 = temp1->next;
rear->next = temp1->next;
temp1->next = front;
front = temp1;
temp1 = temp2;
}
else
{
temp1 = temp1->next;
rear = rear->next;//尾指针往后移动一位
}
}
L->next = front;//将分割好的链表接回到头结点的后面
}
int main(int argc, char* argv[])
{
LinkList* L;
InitList(L);
appendNode(L,3);
appendNode(L, 5);
appendNode(L, 4);
appendNode(L, 1);
appendNode(L, 2);
printf("分割前");
PrintList(L);
DevideL(L,3);
printf("分割后");
PrintList(L);
DestroyList(L);
return 0;
}