C语言实现单链表,单链表面试题面试

单链表是学习不可缺少的一个重要模块,在面试中也会出很多的单链表变种问题,今天就把他们汇总宋总结一下
首先来是实现一个简单的单链表:

(在这里,一些简单的实现单链表的操作函数就不备注了)

typedef  int DataType;//typedef了一个类型,以后如果想要改变单链表节点内储存数据的类型就可以直接在这里改变

typedef struct SListNode
{
DataType data;           //数据
struct SListNode* next;  //指向下一个节点的指针
}SListNode;

SListNode* BuyNode(DataType x)//创建单链表的节点
{
SListNode* temp;
    temp = (SListNode*)malloc(sizeof(SListNode));//为节点开辟一定大小的空间
temp->data = x;
temp->next = NULL;
return temp;
}

void PushBack(SListNode* & pHead, DataType x)
//在单链表末尾插入数据,这时要检查单链表为空和不为空的两种情况,分别处理
{
//1.空
//2.不空
if (pHead == NULL)
{
pHead = BuyNode(x);
}
else 
{
SListNode* Tail = pHead;
while (Tail->next != NULL)
{
Tail = Tail->next;
}
Tail->next = BuyNode(x);
}
}

void PopBack(SListNode* & pHead)
//删除单链表末尾的节点
{
//1.空
//2.一个节点
//3.多节点
if (pHead == NULL)
{
printf("The SList is empty\n");
return;
}
else if (pHead->next == NULL)
{
free(pHead);
pHead = NULL;
}
else
{
SListNode* tail = pHead;
SListNode* prev = NULL;

while (tail->next != NULL)
{

prev = tail;
tail = tail->next;
}

free(tail);
prev->next = NULL;
}
}
//在单链表前面插入一个节点
void PushFront(SListNode* & pHead, DataType x)
{
//1.空
//2.不空
if (pHead == NULL)
{
pHead = BuyNode(x);
}
else if (pHead != NULL)
{
SListNode* cur = BuyNode(x);
cur->next = pHead;
pHead = cur;
}
}
//在单链表前面删除一个节点
void PopFront(SListNode* & pHead)
{
if (pHead == NULL)
{
return;
}
else if (pHead->next == NULL)
{
free(pHead);
pHead = NULL;
}
else if (pHead != NULL)
{
SListNode* cur = pHead;
pHead = pHead->next;
free(cur);
cur = NULL;
}
}
//查找节点并返回它
SListNode* Find(SListNode* pHead, DataType x)
{
SListNode* cur = pHead;
while (cur)
{
if (cur->data == x)
{
return cur;
}

cur = cur->next;
}
return NULL;
}
  //在指定位置插入一个节点
void Insert(SListNode* pos, DataType x)
{
assert(pos);

SListNode* temp = BuyNode(x);
temp->next = pos->next;
pos->next = temp;

}
  //删除指定节点
void Erase(SListNode* pHead,SListNode* pos)
{
assert(pos);
assert(pHead);
if (pHead == pos)
{
pHead = pHead->next;
free(pos);
pos = NULL;
}
SListNode* prev = pHead;
while (prev)
{
if (prev->next == pos)
{
prev->next = pos->next;
free(pos);
pos = NULL;
break;
}
prev = prev->next;
}
}
  //队单链表冒泡排序,(只改变单链表结点的数据)
  void Bubble(SListNode* pHead)
{
int exange = 0;
if (pHead == NULL || pHead->next == NULL)
{
return;
}
SListNode* prev = pHead;
SListNode* cur = pHead->next;
SListNode* Tail =NULL;
while (Tail != pHead)
{
cur = pHead->next;
prev = pHead;
while (cur != Tail)
{
            if (prev->data > cur->data)//比较节点信息,如果满足条件就交换它
{
DataType x;
x = cur->data;
cur->data = prev->data;
prev->data = x;
}
prev = cur;
            cur = cur->next;//指向下一个节点,实现迭代
}
     Tail = prev;//循环结束后prev指向为节点,赋值给Tail
}
}
  //删除一个无头单链表的非尾节点,思想就是交换该节点和下一个节点的信息,删除该节点的后一个节点
void DelNonTailNode(SListNode* pos)
{
assert(pos);
assert(pos->next);

SListNode* del = pos->next;
pos->data = del->data;
pos->next = del->next;
free(del);
del = NULL;
}
  //在无头单链表的非头节点插入一个节点
void InsertNonTailNode(SListNode* pos,DataType x)
{
assert(pos);
SListNode* cur = BuyNode(pos->data);
SListNode* temp = pos->next;
cur->next = temp;
pos->next = cur;
pos->data = x;
}
  //查找单链表的中间节点并只能遍历一次链表(快慢指针)
  //快指针走两步满指针走一步,等到快指针走到尾,则慢指针刚好到中间
SListNode* FindMidNode(SListNode* pHead)
{
assert(pHead);

if (pHead->next == NULL)
{
return pHead;
}
else
{
SListNode* slow = pHead;
SListNode* fast = pHead;
  //在这里我给出了两种写法,都是正确的
        /*while (fast)//快指针不为空,在下面条件判断快指针的下一个节点也不为空
{
if (fast->next)
{
fast = fast->next->next;
}
else
{
break;
}
slow = slow->next;
}*/
       while (fast && fast->next)//注意这里的条件是快指针和快指针的下一个节点都不为空
{
slow = slow->next;
fast = fast->next->next;
}
  return slow;//返回慢节点,也就是中间节点
}
return NULL;
}
  //查找链表的倒数第k个节点,且只能遍历链表一次
  //这也是利用快慢指针,快指针先走k步,慢指针走一步,快指针走一步,则两个指针相差k步,当快指针指向空时,慢指针刚好指向倒数第k个节点
SListNode* FindNode(SListNode* pHead, int k)
{
assert(pHead);
assert(k >= 0);

SListNode* slow = pHead;
SListNode* fast = pHead;
//有问题
/*while (fast && fast->next)
{
while (k--)
{
fast = fast->next;
}
fast = fast->next;
slow = slow->next;
}*/
while (fast && --k)
{
fast = fast->next;
if (fast == NULL)
{
return NULL;
}
}

while (fast->next)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
  //从尾到头打印单链表(利用递归实现)

void PrintTailToHead(SListNode* pHead)
{
if (pHead == NULL)
{
return;
}
else
{
PrintTailToHead(pHead->next);
printf("%d ", pHead->data);
}
}
  //逆置单链表   很重要,创建新的头节点,把单链表的每个节点摘下来插入
它前面,再把它往前挪构成一个新的单链表,返回新链表的头节点
SListNode*  Reverse(SListNode* pHead)
{
SListNode* cur = pHead;
SListNode* newHead = NULL;

while (cur)
{
SListNode* temp = cur;
cur = cur->next;

temp->next = newHead;
newHead = temp;
}
return newHead;
}
  //单链表实现约瑟夫环
//运行时先构造环,注意在代码结尾解环
SListNode* JosePhCycle(SListNode* pHead, int m)
{
SListNode* cur = pHead;
while (1)
{
if (cur == NULL)
{
return NULL;
}
else if (cur == cur->next)//只剩一个节点
{
return cur;
}
else
{
int x = m;
            while (--x)//指向第m各节点并删除它,循环程序直到只剩一个节点的时候返回它
{
cur = cur->next;
}
SListNode* del = cur->next;
cur->data = del->data;
cur->next = del->next;
            free(del);//释放节点并置空
del = NULL;
}
}

}
  //合并两个有序链表,合并后依然有序,摘下一个节点,比较大小插入到新链表的尾部,构成一个新的单链表,返回新链表的头节点

SListNode* Meragelist(SListNode* pHead1, SListNode* pHead2)
{
if (pHead1 == NULL)
{
return pHead2;
}
if (pHead2 == NULL)
{
return pHead1;
}
//SListNode* newHead = pHead1->data < pHead2->data ? pHead1:pHead2;

//while (pHead1 == NULL || pHead2 == NULL)
//{
//if (pHead1->data < pHead2->data)
//{
//newHead = pHead1;
//pHead1 = pHead1->next;
//}
//else if (pHead1->data == pHead2->data)
//{
//newHead = pHead1;
//newHead->next = pHead2;
//pHead1 = pHead1->next;
//pHead2 = pHead2->next;
//}
//else
//{
//newHead = pHead2;
//pHead2 = pHead2->next;
//}
//newHead = newHead->next;
//newHead->next = NULL;
//}
//while (pHead1)
//{
//newHead->next = pHead1;
//pHead1 = pHead1->next;
//}
//while (pHead2)

SListNode* newHead = NULL;
SListNode* cur1 = pHead1;
SListNode* cur2 = pHead2;
if (cur1->data < cur2->data)
{
newHead = cur1;
cur1 = cur1->next;
}
SListNode* Tail = newHead;
while (cur1 && cur2)
{
if (cur1->data < cur2->data)
{
Tail->next = cur1;
cur1 = cur1->next;
}
else
{
Tail->next = cur2;
cur2 = cur2->next;
}
Tail = Tail->next;
Tail->next = NULL;
}
if (cur1 != NULL)
{
Tail->next = cur1;
}
if (cur2 != NULL)
{
Tail->next = cur2;
}
return newHead;
}

  /判断链表是否带环(可以用快慢指针解决),快指针走两步,慢指针走一步,看看是否会相遇,相遇了则是带环,不想与则不带环
bool IsSListCycle(SListNode* pHead)
{
if (pHead == NULL)
{
printf("The SList is
empty\n");
return false;
}
SListNode* slow = pHead;
SListNode* fast = pHead;

while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
SListNode* cur = slow;
int length = 0;       //环的长度
do
{
slow = slow->next;
length++;

} while (cur != slow);

printf("%d\n", length);
printf("The SList have cycle\n");
return true;
}
}
printf("The SList NO cycle\n");
return false;
}

//环的入口点

SListNode* CycleEntry(SListNode* pHead)
{
if (pHead == NULL)
{
printf("The SList is empty\n");
return NULL;
}
SListNode* slow = pHead;
SListNode* fast = pHead;

while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return slow;
}
}
}

//链表是否相交,若相交,求交点(不带环)指向同一个节点,并不是相同信息的节点

SListNode* IsItersect(SListNode* pHead1,SListNode* pHead2)
{
if (pHead1 == NULL)
{
printf("链表不相交\n");
return pHead2;
}
if (pHead2 == NULL)
{
printf("链表不相交\n");
return pHead1;
}
SListNode* cur1 = pHead1;
SListNode* cur2 = pHead2;

while (cur1 && cur2)
{
if (cur1 == cur2)
{
printf("链表相交,交点为:%d\n", cur1->data);
return cur1;
}
cur1 = cur1->next;
cur2 = cur2 ->next;
}
return NULL;
}
时间: 2024-11-16 23:14:40

C语言实现单链表,单链表面试题面试的相关文章

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

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

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,要是我想在尾部添加一个节点,是不是爬也要爬到尾部去啊... 这个是简单版中提出

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

//单链表的基本数据类型及其结构 typedef int DataType; typedef struct LinkNode { DataType data; struct LinkNode *next; }LinkNode,*pLinkNode,*pLinkList; //单链表的初始化 void InitLinkList(pLinkList* pHead)//初始化 { assert(pHead); *pHead = NULL; } //当尾插,头插,插入时都需要建立一个新的结点,为方便建立

c/c++单链表面试题—链表带环问题

1.判断一个单链表是否带环 思路解析: 判断一个单链表是不是带环,就看在遍历单链表的时候能不能遍历完成,如果带环的话会陷入死循环程序一直无法结束,但是这种判断方法在程序的实现是不可能的.所以转换一种思路,利用两个遍历速度不同的指针遍历,如果存在环的话,那么快指针迟早会追上慢指针.通过这个判断程序实现起来是比较简单可行的. 单链表的结构体及其类的定义 struct Node { public: Node(const DataType& d) :_data(d) ,_next(NULL) {} pu

c/c++单链表面试题—链表相交问题

1.判断两个单链表是不是相交 思路分析: 最简单直接的方法就是依次遍历两条链表,判断其尾节点是不是相等,相等则相交否则不相交. bool CheckCross(const List& list1, const List& list2)//list1,list2为两个对象 { Node* l1 = list1._head; Node* l2 = list2._head; while (l1->_next)//找到list1的尾节点 { l1 = l1->_next; } whil

【C语言】单链表的相关热点面试题(包括:从尾到头打印,逆置,冒泡,寻找中间节点,倒数k节点)

从尾到头打印单链表 void FromTailToHeadPrint(SListNode*& head) {     stack<SListNode*> s;     SListNode* cur = head;     while (cur)     {         s.push(cur);         cur = cur->_next;     }     while (!s.empty())     {         cout << s.top()-&