C语言实现单链表的基本操作及其部分面试题

//单链表的基本数据类型及其结构

typedef int DataType;

typedef struct LinkNode

{

DataType data;

struct LinkNode *next;

}LinkNode,*pLinkNode,*pLinkList;

//单链表的初始化

void InitLinkList(pLinkList* pHead)//初始化

{

assert(pHead);

*pHead = NULL;

}

//当尾插,头插,插入时都需要建立一个新的结点,为方便建立一个创建结点的函数

pLinkNode BuyNode(DataType d)//创建一个结点

{

pLinkNode newNode = (pLinkNode)malloc(sizeof(LinkNode));

newNode->data = d;

newNode->next = NULL;

return newNode;

}

//单链表的销毁,即把单链表中所有的结点都释放掉

void Destory(pLinkList* pHead)//销毁

{

if(*pHead == NULL)

{

return;

}

else

{

pLinkNode cur = *pHead;

while (cur)

{

pLinkNode del = cur;

cur = cur->next;

free(del);

del = NULL;

}

}

}

//在当前单链表的末尾插入一个结点

void PushBack(pLinkList* pHead,DataType d)//尾插

{

pLinkNode cur = *pHead;

pLinkNode newNode = BuyNode(d);

assert(pHead);

if(cur == NULL)

{

*pHead = newNode;

return ;

}

while(cur->next)

{

cur = cur->next;

}

cur->next = newNode;

}

//把当前单链表的最后一个结点删除掉

void PopBack(pLinkList* pHead)//尾删

{

pLinkNode cur = *pHead;

assert(pHead);

if(*pHead == NULL)

{

return;

}

else if((*pHead)->next == NULL)

{

free(*pHead);

*pHead = NULL;

}

else

{

pLinkNode del;

while (cur->next->next)

{

cur = cur->next;

}

del = cur->next;

cur->next = NULL;

free(del);

del = NULL;

}

}

//在当前单链表的头部插入一个结点

void PushFront(pLinkList* pHead,DataType d)//头插

{

pLinkNode newNode = BuyNode(d);

pLinkNode cur = *pHead;

assert(pHead);

if(cur == NULL)

{

*pHead = newNode;

return;

}

else

{

newNode->next = *pHead;

*pHead = newNode;

}

}

//将单链表的第一个结点删除掉

void PopFront(pLinkNode* pHead)//头删

{

assert(pHead);

if(*pHead == NULL)

{

return;

}

else

{

pLinkNode del = *pHead;

*pHead = (*pHead)->next;

free(del);

del = NULL;

}

}

//获取单链表的长度,增加一个计数器,将单链表遍历一遍

int GetListLength(pLinkList* pHead)//求单链表的长度

{

pLinkNode cur = *pHead;

int count = 0;

assert(pHead);

while(cur)

{

count++;

cur = cur->next;

}

return count;

}

//给定单链表的一个结点,在该结点的位置插入一个新的节点,遍历单链表,只要(cur->next==pos),使新结点的next指向pos,再将新结点赋给cur->next。

void InsertList(pLinkList* pHead,pLinkNode pos,DataType d)//插入

{

pLinkNode newNode = BuyNode(d);

pLinkNode cur = *pHead;

assert(pHead);

assert(pos);

if(*pHead == pos)

{

newNode->next = *pHead;

*pHead = newNode;

}

else

{

while (cur->next != pos)

{

cur = cur->next;

}

newNode->next = cur->next;

cur->next = newNode;

}

}

//遍历单链表,只要某结点的值与要找的值相等,则返回该结点,否则返回NULL

pLinkNode Find(pLinkList* pHead,DataType d)//查找

{

pLinkNode cur = *pHead;

assert(pHead);

while(cur)

{

if(cur->data == d)

{

return cur;

}

cur = cur->next;

}

return NULL;

}

//方法1 利用Find函数找到需要删除的结点pos,遍历单链表,只要(cur->next == pos),将pos->next赋给cur->next,再将pos释放掉。

void Remove(pLinkList* pHead,DataType d)//删除结点

{

pLinkNode pos = Find(pHead,d);

pLinkNode cur = *pHead;

pLinkNode del = NULL;

assert(pHead);

if(*pHead == pos)

{

del = *pHead;

*pHead = (*pHead)->next;

free(del);

del = NULL;

}

else

{

while (cur->next != pos)

{

cur = cur->next;

}

del = cur->next;

cur->next = del->next;

free(del);

}

}

//方法2 不需要已有函数Find,原理与方法1一样

void Remove(pLinkList* pHead,DataType d)//删除结点

{

pLinkNode cur = *pHead;

pLinkNode del = NULL;

pLinkNode prev = NULL;

assert(pHead);

if(cur == NULL)

{

return;

}

while(cur)

{

if(cur->data == d)

{

del = cur;

if(cur == *pHead)

{

*pHead = (*pHead)->next;

}

else

{

prev->next = cur->next;

}

free(del);

break;

}

prev = cur;

cur = cur->next;

}

}

//删除单链表中所有相同的结点,首先,遍历单链表,找到第一个要删除的结点,删除并释放掉,同时增加一个指向该结点下一个结点的指针,以此位置向后遍历单链表,删除相同结点,以此类推。

void RemoveAll(pLinkList* pHead,DataType d)//删除所有相同结点

{

pLinkNode cur = *pHead;

pLinkNode del = NULL;

pLinkNode prev = NULL;

assert(pHead);

while(cur)

{

if(cur->data == d)

{

del = cur;

if(cur == *pHead)

{

*pHead = (*pHead)->next;

cur = *pHead;

}

else

{

prev->next = cur->next;

cur = prev->next;

}

free(del);

}

else

{

prev = cur;

cur = cur->next;

}

}

}

//删除指定位置的结点,需找到要删除结点的上一个结点,将该结点的下一个结点赋给该结点的next,删除并释放该结点。

void Erase(pLinkNode* pHead,pLinkNode pos)//删除指定位置的结点

{

pLinkNode cur = *pHead;

pLinkNode del = NULL;

assert(pHead);

if(*pHead == NULL)

{

return;

}

else if(*pHead == pos)

{

free(*pHead);

*pHead =NULL;

}

else

{

while(cur->next)

{

if(cur->next == pos)

{

del = cur->next;

cur->next = del->next;

free(del);

del = NULL;

}

cur = cur->next;

}

}

}

//删除无头单链表的非尾结点,首先给出一个指针del记录pos->next,将pos->data赋给pos->data,再将del->next赋给pos->next,最后只需把del释放即可

void EraseNotTail(pLinkNode pos)//删除无头单链表的非尾结点

{

pLinkNode del = pos->next;

pos->data = del->data;

pos->next = del->next;

free(del);

del = NULL;

}

//单链表的逆序,把第一个结点取出来,他的next恒等于NULL,然后再把其他结点依次取出来利用头插的方法插入,完成逆序。

void ReverseList(pLinkList* pHead)//反转(逆序)

{

pLinkNode pNewHead = NULL;

pLinkNode cur = *pHead;

pLinkNode prev = NULL;

assert(pHead);

if((cur == NULL)&&(cur->next == NULL))

{

return;

}

while(cur)

{

prev = cur;

cur = cur->next;

prev->next = pNewHead;

pNewHead = prev;

}

*pHead = pNewHead;

}

//冒泡排序

void BubbleSort(pLinkList* pHead)//排序链表(冒泡)

{

pLinkNode cur = *pHead;

assert(pHead);

while(cur)

{

pLinkNode pos =cur->next;

while(pos)

{

if(cur->data  > pos->data)

{

DataType tmp = cur->data;

cur->data = pos->data;

pos->data = tmp;

}

pos = pos->next;

}

cur = cur->next;

}

}

//在当前结点前插入一个结点,首先在pos位置后插入一个结点,然后再交换pos->data和newNode->data即可

void InsertFrontNode(pLinkNode pos,DataType d)//在当前结点前插入一个数据

{

pLinkNode NewNode = BuyNode(d);

DataType tmp = 0;

assert(pos);

NewNode->next = pos->next;

pos->next = NewNode;

tmp = pos->data;

pos->data = NewNode->data;

NewNode->data = tmp;

}

//合并两个有序链表(非递归)

pLinkNode Merge(pLinkList l1,pLinkList l2)

{

pLinkNode newHead = NULL;

pLinkNode cur = NULL;

if(l1 == l2)

{

return l1;

}

if((l1 != NULL)&&(l2 ==NULL))

{

return l1;

}

if((l1 == NULL)&&(l2 != NULL))//确定头节点

{

return l2;

}

if(l1->data < l2->data)

{

newHead = l1;

l1 = l1->next;

}

else

{

newHead =l2;

l2 = l2->next;

}

cur = newHead;

while ((l1)&&(l2))

{

if(l1->data < l2->data)

{

cur->next = l1;

l1 = l1->next;

}

else

{

cur->next = l2;

l2 = l2->next;

}

cur = cur->next;

}

if(l1)

{

cur->next = l1;

}

else

{

cur->next =l2;

}

return newHead;

}

//利用递归实现链表的合并

pLinkNode _Merge(pLinkList plist1,pLinkList plist2)

{

pLinkNode newHead = NULL;

if((plist1 == NULL) &&(plist2 == NULL))

{

return NULL;

}

else if((plist1 != NULL)&&(plist2 == NULL))

{

return plist1;

}

else if((plist1 == NULL)&&(plist2 != NULL))

{

return plist2;

}

if(plist1->data < plist2->data)

{

newHead = plist1;

newHead->next = _Merge(plist1->next,plist2);

}

else

{

newHead = plist2;

newHead->next = _Merge(plist1,plist2->next);

}

return newHead;

}

//利用快慢指针,快指针走两步,慢指针走一步,返回慢指针,即为中间结点

pLinkNode FindMidNode(pLinkList* pHead)//查找链表的中间结点

{

pLinkNode fast = *pHead;

pLinkNode slow = *pHead;

while (fast && fast->next)

{

fast = fast->next->next;

slow = slow->next;

}

return slow;

}

//利用快慢指针,快指针先走k-1步后,两指针同时走,慢指针即为倒数第k个结点,然后再利用删除无头单链表的非尾结点中的方法删除慢指针的下一个结点。

void DelKNode(pLinkList *pHead,int k)//删除单链表的倒数第k(k>1)个结点

{

pLinkNode fast = *pHead;

pLinkNode slow = *pHead;

pLinkNode del = NULL;

assert(*pHead);

assert(k>1);

while (--k)

{

fast = fast->next;

}

while(fast->next)

{

fast = fast->next;

slow = slow->next;

}

del = slow->next;

slow->data = slow->next->data;

slow->next = slow->next->next;

free(del);

}

//约瑟夫环,n个人围成一圈,从某个人开始报数,报到m的那个人退出,然后m的下一个人继续从1开始报数,报到m的那个人退出,以此类推,最后剩下的那个人胜出。

//设置一个指针变量指向第一个结点,从第一个结点先走(num-1)步,利用删除单链表的无头非尾结点的方法,删除num结点的下一个结点,依次类推。

pLinkNode JosephCycle(pLinkList *pHead,int num)

{

pLinkNode cur = *pHead;

pLinkNode del = NULL;

int count = 0;

assert(pHead);

while(1)

{

count = num;

if(cur->next == cur)

{

break;

}

while(--count)

{

cur = cur->next;

}

printf("%d ",cur->data);

del = cur->next;

cur->data = cur->next->data;

cur->next = cur->next->next;

free(del);

}

*pHead = cur;

return cur;

}

//利用快慢指针,快指fast针走两步,慢指针slow走一步,只要(fast == slow),返回slow,则单链表带环,否则不带环。

pLinkNode CheckCycle(pLinkList pList)//判断链表是否带环

{

pLinkNode fast = pList;

pLinkNode slow = pList;

while (fast && fast->next)

{

slow = slow->next;

fast = fast->next->next;

if(fast == slow)

{

return slow;

}

}

return NULL;

}

//增加一个计数器,从相遇结点在遍历一遍环即可求的环的长度

int GetCircleLength(pLinkNode meet)//若带环则根据相遇结点求环的长度

{

int count = 0;

pLinkNode pos = meet;

do

{

pos = pos->next;

count++;

}while (pos != meet);

return count;

}

//增设两个指针,一个指向单链表的第一个结点,一个指向相遇结点,只要两者不等,同时各走一步,终有一次两者相等,则返回环的入口结点

pLinkNode GetCycleEntryNode(pLinkList pList,pLinkNode meetNode)//获取环的入口结点

{

pLinkNode entry = pList;

pLinkNode meet = meetNode;

while (entry != meet)

{

entry = entry->next;

meet = meet->next;

}

return entry;

}

时间: 2024-08-09 10:38:58

C语言实现单链表的基本操作及其部分面试题的相关文章

C语言实现单链表(带头结点)的基本操作

我在之前一篇博客<C语言实现单链表(不带头结点)的基本操作>中具体实现了不带头结点的单链表的11种操作:如计算链表长度.初始化.创建链表.清空链表等等.但是在实际使用中,带头结点的单链表往往比不带头结点的单链表用的更多,使用也更为方便.因为不用单独考虑第一个节点的情况了,第一个节点和其他后续节点的处理全都一样了,简化操作.这篇博客将会来实现带头结点的单链表的11种操作.代码上传至: https://github.com/chenyufeng1991/LinkedList_HeadNode  .

单链表的基本操作实现

1.写这篇博文的原因 C语言有三个重要部分:流程控制.函数.指针. 对于指针,单单了解它的简单运用是远远不够的,最重要的是学习链表.所以这篇文章通过用C语言实现链表的一些基本操作和总结,希望对C语言的指针有更新的理解和认识. 2.单链表的定义 单链表是通过一组任意的存储单元来存储线性表中的数据元素,这些存储单元可以是连续的也可以是不连续的.为了建立起数据元素之间的关系,对于每个数据元素除了存放元素自身的信息外,还必须有包含指向该元素直接后继元素位置的信息,这两部分信息组成一个节点,即每个节点都有

C语言实现单链表节点的删除(带头结点)

我在之前一篇博客<C语言实现单链表节点的删除(不带头结点)>中具体实现了怎样在一个不带头结点的单链表的删除一个节点,在这一篇博客中我改成了带头结点的单链表.代码演示样例上传至 https://github.com/chenyufeng1991/DeleteLinkedList_HeadNode.删除类型有两种: (1)删除某个位置pos的节点: (2)推断x值是否在链表中,若存在则删除该节点: 核心代码例如以下: //删除某个位置pos的节点 Node *DeletePosNode(Node

C语言实现单链表的节点插入(带头结点)

我在之前一篇博客<C语言实现单链表(不带头结点)节点的插入>中具体实现了怎样在一个不带头结点的单链表中进行节点的插入.可是在实际应用中,带头结点的链表更为经常使用.更为方便.今天我们就要来使用带头结点的单链表进行节点的插入.演示样例代码上传至 https://github.com/chenyufeng1991/InsertList_HeadNode  . 核心代码例如以下: Node *InsertNode(Node *pNode,int pos,int x){ int i = 0; Node

C语言实现单链表-03版

在C语言实现单链表-02版中我们只是简单的更新一下链表的组织方式: 它没有更多的更新功能,因此我们这个版本将要完成如下功能: Problem 1,搜索相关节点: 2,前插节点: 3,后追加节点: 4,一个专门遍历数据的功能: Solution 我们的数据结构体定义如下,和上一个版本稍微有所不同: 例如,我们把人这个结构体添加一个name域,前几个版本没有名字的节点: 我们叫起来很尴尬,都是第几个第几个节点,好啦!现在有名字啦! #include <stdio.h> #include <s

C语言实现单链表-02版

我们在C语言实现单链表-01版中实现的链表非常简单: 但是它对于理解单链表是非常有帮助的,至少我就是这样认为的: 简单的不能再简单的东西没那么实用,所以我们接下来要大规模的修改啦: Problem 1,要是数据很多怎么办,100000个节点,这个main函数得写多长啊... 2,这个连接的方式也太土啦吧!万一某个节点忘记连接下一个怎么办... 3,要是我想知道这个节点到底有多长,难道每次都要从头到尾数一遍嘛... 4,要是我想在尾部添加一个节点,是不是爬也要爬到尾部去啊... 这个是简单版中提出

用语言写单链表

#pragma once #include<stdio.h> #include<assert.h> #include<malloc.h> #include<stdlib.h> typedef int DataType; typedef struct Node { DataType _data; // 数据 struct Node* _next; // 指向下一个节点的指针 }Node, *PLinkList; //typedef struct Node  N

单链表的基本操作(第一篇)

  学习数据结构的笔记 1.在线性表的链式存储中,头指针与头结点之间的根本区别以及头结点与开始结点的关系: 链表的头指针一般指向其第一个结点,他有标识链表的作用.头结点的作用在于统一相关操作,头结点的数据域一般没有意义,在某些情况下可以存放链表长   度.如果链表含有头结点,无论链表是   否为空,头指针均不为空.开始结点也就是链表中存储数据的第一个结点,它是头结点后边的第一个结点. 2.比较顺序表和链表各自的特点: (1)存储分配的方式:顺序表的存储空间是静态分配的.链表的存储空间是动态分配的

「C语言」单链表/双向链表的建立/遍历/插入/删除

最近临近期末的C语言课程设计比平时练习作业一下难了不止一个档次,第一次接触到了C语言的框架开发,了解了View(界面层).Service(业务逻辑层).Persistence(持久化层)的分离和耦合,一种面向过程的MVC的感觉. 而这一切的基础就在于对链表的创建.删除.输出.写入文件.从文件读出...... 本篇文章在于巩固链表的基础知识(整理自<C语言程序设计教程--人民邮电出版社>第十章),只对链表的概念及增删改查作出探讨,欢迎指教. 一.链表结构和静态/动态链表 二.单链表的建立与遍历