单链表面试题

#pragma once

#include<stdio.h>

#include<assert.h>

#include<stdlib.h>

#include<Windows.h>

#include<math.h>

 

typedef int DataType;

typedef struct SListNode {

DataType data; //

struct SListNode *pNext; // 指向下一个结点

} SListNode;

 

// 初始化

void SListInit(SListNode **ppFirst)

{

assert(ppFirst != NULL);

*ppFirst = NULL;

}

 

SListNode *SListFind(SListNode *pFirst, DataType data)

{

SListNode *pNode;

for (pNode = pFirst; pNode; pNode = pNode->pNext){

if (pNode->data = data){

return pNode;

}

}

return NULL;

}

 

// 尾部插入

//尾插:申请空间(data=datapNext=NULL),找到倒数第一个,倒数第一个的pNEXT=NULL

void SListPushBack(SListNode** ppFirst, DataType data)

{

assert(ppFirst != NULL);

SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));

assert(pNewNode != NULL);

pNewNode->data = data;

pNewNode->pNext = NULL;

if (*ppFirst == NULL)

{

*ppFirst = pNewNode;

}

SListNode *pNode;

pNode = *ppFirst;

while(pNode->pNext!= NULL)

{

pNode = pNode->pNext;

}

//倒数第一个

pNode->pNext = pNewNode;

pNewNode->pNext = NULL;

 

}

 

// 头部插入

void SListPushFront(SListNode **ppFirst, DataType data)

{

assert(ppFirst != NULL);

SListNode *pNewNode = (SListNode*)malloc(sizeof(SListNode));

assert(pNewNode != NULL);

pNewNode->data = data;

pNewNode->pNext = NULL;

if (*ppFirst == NULL){

SListPushBack(ppFirst, data);

}

pNewNode->pNext = *ppFirst;

*ppFirst = pNewNode;

}

 

 

//打印

void print(SListNode *pFirst)

{

SListNode *pNode;

for (pNode = pFirst; pNode; pNode = pNode->pNext)

{

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

}

printf("NULL");

printf("\n");

}

 

 

 

//单链表面试题

 

//1.打印单链表

//1)逆置打印:先找到链表尾部,

//思路:先找到最后一个,打印最后一个,然后指向链表最后一个指针前移,知道遇到链表第一个节点。时间复杂度O(n^2),空间复杂度O(1)

void PrintTail(SListNode *pFirst)

{

SListNode *end = NULL;

while (end != pFirst){

SListNode *pNode = pFirst;

while (pNode->pNext != end){

pNode = pNode->pNext;

}

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

end = pNode;

}

printf("\n");

}

//2)递归实现 时间复杂度:O(n),空间复杂度:O(n)

void PrintTailR(SListNode *pFirst)

{

if (pFirst == NULL)

return;

PrintTailR(pFirst->pNext);

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

}

 

 

 

//2.删除一个无头单链表的非尾节点(不能遍历链表)

//思路:有头但是不告诉你,给一个节点位置,不一定要删除这个节点,但可以删除给定节点后面的那个节点

void DelNode(SListNode *pPos)

{

assert(pPos && pPos->pNext );

//替换法进行删除

SListNode *next = pPos->pNext;

pPos->data = next->data;

pPos->pNext = next->pNext;

free(next);

}

 

 

 

//3.在无头单链表的一个节点前插入一个节点(不能遍历链表)

//思路:将插入的数查到指定位置后面,因为我们不知道给定位置前面的值,所以只能插入节点的值和指定位置的值相等,然后将要要插入的值放到给定位置的值

void InsertNode(SListNode *pos,DataType data)

{

assert(pos);

SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));

pNewNode->data = pos->data;

pNewNode->pNext = pos->pNext;

pos->pNext = pNewNode;

pos->data=data;

}

 

 

 

//4.单链表实现约瑟夫环

/*约瑟夫环:已知n个人(以编号123...n分别表示)围坐在一张圆桌周围。

从编号为k的人开始报数,数到m的那个人出列;

他的下一个人又从1开始报数,数到m的那个人又出列;

依此规律重复下去,直到圆桌周围的人全部出列

*/

//思路:

SListNode *JosephCycle(SListNode *pFirst,int k)    //数到k的那个删掉

{

SListNode *pNode =pFirst;

SListNode *p = pFirst;

if (pFirst == NULL)

return NULL;

 

while (pNode->pNext)

{

pNode=pNode->pNext;

}//找到结尾

pNode->pNext = pFirst;    //构成环

 

 

//约瑟夫环结束

while (p->pNext!=p)

{

int count = k;

while (--count){

p = p->pNext;

 

//替换法删除

SListNode *next = p->pNext;

p->data = next->data;

p->pNext = next->pNext;

 

free(next);

}

}

return p;

}

 

 

 

 

//5.单链表的逆置/反转

//思路:

//思路一:定义三个指针:n1,n2,n3分别指向第一,二,三个元素

//可用返回值,也可用二级指针

SListNode *reserveNode(SListNode *pFirst)

{

SListNode *n1, *n2, *n3;

if (pFirst== NULL || pFirst->pNext == NULL)

return pFirst;

n1 = pFirst;

n2 = n1->pNext;

n3 = n2->pNext;

while (n2)

{

n2->pNext = n1;

n1 = n2;

n2 = n3;

if (n3)

n3 = n3->pNext;

}

pFirst->pNext=NULL;

pFirst = n1;

return pFirst;

}

//思路二:头插法:定义两个指针一个pNewNode指向空,pNode指向链表头,pNode遍历链表,

//在遍历的时候再定义一个指针来保存pNode遍历的值,然后头插到节点pNewNode上,而不是创一个新链表

SListNode *reserveNode1(SListNode *pFirst)

{

SListNode *pNewNode = NULL;

SListNode *pNode=pFirst;

while (pNode)

{

SListNode *next = pNode;

pNode = pNode->pNext;

 

//头插

next->pNext = pNewNode;

pNewNode = next;

}

return pNewNode;

}

 

 

 

//6.单链表的排序(冒泡排序&&选择排序)

//冒泡排序

//思路:定义三个指针:pNode指向链表头,next指向第二个元素,tail指向的每轮结束的位置,开始为NULL。然后比较pNodenext的值,大就交换,当next为空时第一轮结束,记录这个位置,下一趟排序只要next遇到这个位置就结束

void BubbleNode(SListNode *pFirst)

{

SListNode *pNode;

SListNode *next;

SListNode *tail=NULL;  //标记终止位置

 

if (pFirst == NULL || pFirst->pNext == NULL)

{

return;

}

while (tail!= pFirst->pNext)

{

DataType flag = 0;

pNode = pFirst;

next = pNode->pNext;

while (next!= tail)

{

if (pNode->data > next->data)

{

flag = 1;

DataType tmp = pNode->data;

pNode->data = next->data;

next->data = tmp;

}

pNode = pNode->pNext;

next = next->pNext;

}

if (flag == 0)

break;

tail = pNode;

}

}

 

 

//7.合并两个有序链表,合并后链表依旧有序

//思路:定义两个指针分别指向两个链表,每次在小的后面尾插.定义一个新链表,

//将两个链表中第一个元素小的地址拿下来作为新链表的头。

//在定义一个指针,指向新链表的尾部

SListNode *MergeList(SListNode *list1,SListNode *list2)

{

SListNode *Newlist;

SListNode *tail;

if (list1 == NULL)

return list2;

if (list2 == NULL)

return list2;

 

 

//list1list2中小的一个用来作为新链表的头

if(list1->data <list2->data)

{

Newlist = list1;

list1 = list1->pNext;

}

else

{

Newlist = list2;

list2=list2->pNext;

}

 

//刚开始尾指向新链表头

tail = Newlist;   

while (list1&&list2)

{

if (list1->data < list2->data)  //list1的值小就将list1尾插到tail后面

{

tail->pNext = list1;

list1 = list1->pNext;

}

else

{//list2的值小就将list2尾插到tail后面

tail->pNext= list2;

list2 = list2->pNext;

}

tail = tail->pNext;

}

 

//走到这里说明list1list2为空

if (list1)                           //如果list1不为空,说明list2为空,就将list1插入到新链表尾部后面

tail->pNext = list1;

if (list2) //如果list2不为空,说明list1为空,就将list2插入到新链表尾部后面

tail->pNext = list2;

 

return Newlist;

}

 

 

 

//8.查找链表的中间节点,只能遍历一次链表

//思路:定义两个指针来遍历链表:指针pFast一次遍历两个元素,指针pSlow一次遍历一个元素

SListNode * FindMidNode(SListNode *pFirst)

{

SListNode *pFast=pFirst;

SListNode *pSlow = pFirst;

if (pFirst == NULL)

return NULL;

 

//快指针一次走两步,慢指针一次走一步

while (pFast&&pFast->pNext)//pFast第一步不为空,第二步也不为空。两个条件次序不能换

{

pFast = pFast->pNext->pNext;

pSlow = pSlow->pNext;

}

return pSlow;

}

 

 

//9.查找倒数第k个节点,只能遍历一次

//思路:给定两个指针:指针pFast先朝前走k步,

SListNode *FindKNode(SListNode *pFirst,DataType k)

{

SListNode *pFast = pFirst;

SListNode *pSlow = pFirst;

if (pFirst == NULL || k <= 0)

return NULL;

 

//(1)pFastk步时,即k--

#if 1

while (k--)  //走到k的位置

{

if (pFast == NULL)

return NULL;

pFast = pFast->pNext;

}

while (pFast)

{

pFast = pFast->pNext;

pSlow = pSlow->pNext;

}

#endif

 

 

//pFastk-1步时,即--k

#if 0

while (--k)   //走到k-1的位置

{

if (pFast == NULL)

return NULL;

pFast = pFast->pNext;

}

while (pFast->pNext )

{

pFast = pFast->pNext;

pSlow = pSlow->pNext;

}

#endif

 

 

return pSlow;

}

 

 

 

//10.判断链表是否带环,若带环求长度,求入口,并计算时间和空间复杂度

//思路:

//怎么判断带环:定义两个指针:指针pFast一次走两步,pSlow一次走一步,如果pFast不为空,则为环

 

//求环的长度:找到在环中两指针相遇点,然后定义两个指针,一个从头开始走,一个从相遇点开始走,两个指针相遇,则是链表尾节点,可求出长度

 

SListNode *IsCycle(SListNode *pFirst)

{

SListNode *pFast=pFirst;

SListNode *pSlow=pFirst;

 

//判断是否为环,并且返回入口点

while (pFast &&pFast->pNext)

{

pSlow = pSlow->pNext;

pFast = pFast->pNext->pNext;

if (pFast == pSlow)

return pSlow;

}

 

return NULL;

}

 

 

////入口点:

/*

假设环的长度为:C

入环前的长度为:L

入口点到相遇点长度:X

慢指针和快指针的关系:L+X=L+X+NC(NpFast在和pSlow相遇前在环中走的环数,N大环小,N小环大)

即:L=NC-X

*/

 

SListNode *GetEntry(SListNode *pFirst,SListNode *meet)  

{

while (pFirst != meet)

{

pFirst = pFirst->pNext;

meet = meet->pNext;

}

return meet;

}

 

//11.判断两个链表是否相交,若相交,求交点(假设链表不带环)

//思路:找尾,尾相同就相交,尾不相同就不想交

//求交点:相交前的长度不一样,让长的链表减去短的链表长度,得到差值,

//然后让长的链表先走差值的长度,最后在同时走,直到相遇

SListNode *GetNode(SListNode *list1,SListNode *list2)

{

SListNode *pList1 = list1;

SListNode *pList2 = list2;

SListNode *shortlist, *longlist;

DataType L1=0, L2=0,L;

if (pList1 == NULL || pList2 == NULL)

return NULL;

while (pList1){

pList1 = pList1->pNext;

++L1;

}

while (pList2){

pList2 = pList2->pNext;

++L2;

}

longlist = list1;

shortlist = list2;

if (L1 < L2){

longlist = list2;

shortlist = list1;

}

L = abs(L1 - L2);   //取绝对值

while (L--){

longlist = longlist->pNext;

}

while (longlist != shortlist){

longlist = longlist->pNext;

shortlist = shortlist->pNext;

}

return longlist;

}

 

 

 

 

//12.判断两个链表是否相交,若相交,求交点(假设链表不带环)

//思路:

//先分类:

/*

(1)都不带环

(2)如果一个带环,一个不带换,肯定不相交

(3)都带环

  a.判断相遇点是否在同一环

*/

 

 

 

 

void test()

{

SListNode *ppFirst;

 

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

print(ppFirst);

 

 

//面试题函数

 

//1.逆置打印链表

PrintTail(ppFirst);

PrintTailR(ppFirst);

printf("\n");

}

void test1(){

 

SListNode *ppFirst;

SListNode *pPos;

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

// 头部插入

SListPushFront(&ppFirst, 2);

print(ppFirst);

 

 

//2.删除一个无头单链表的非尾节点

pPos = SListFind(ppFirst,3);

DelNode(pPos);

print(ppFirst);

 

}void test2(){

 

SListNode *ppFirst;

SListNode *pPos;

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

// 头部插入

SListPushFront(&ppFirst, 2);

print(ppFirst);

 

 

//3.在无头单链表的一个节点前插入一个节点(不能遍历链表)

pPos = SListFind(ppFirst, 3);

InsertNode(pPos, 1);

InsertNode(pPos, 4);

print(ppFirst);

 

}

void test3(){

 

SListNode *ppFirst;

SListNode *pPos;

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 1);

SListPushBack(&ppFirst, 2);

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

print(ppFirst);

 

//4.约瑟夫环问题

pPos = JosephCycle(ppFirst, 2);

printf("%d\n", pPos->data );

}

void test4()

{

SListNode *ppFirst;

SListNode *pPos;

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 1);

SListPushBack(&ppFirst, 2);

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

print(ppFirst);

 

 

//5单链表逆置

pPos= reserveNode1(ppFirst);

print(pPos);

}

void test5()

{

SListNode *ppFirst;

// 初始化

SListInit(&ppFirst);

// 尾部插入

SListPushBack(&ppFirst, 8);

SListPushBack(&ppFirst,3);

SListPushBack(&ppFirst, 7);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 2);

SListPushBack(&ppFirst, 6);

print(ppFirst);

 

//6.冒泡排序

BubbleNode(ppFirst);

print(ppFirst);

}

 

void test6()

{

 

SListNode *p;

SListNode *list1 = NULL;

SListNode *list2 = NULL;

// 尾部插入

SListPushBack(&list1, 1);

SListPushBack(&list1, 3);

SListPushBack(&list1, 5);

SListPushBack(&list1, 7);

print(list1);

 

 

SListPushBack(&list2, 2);

SListPushBack(&list2, 4);

SListPushBack(&list2, 6);

SListPushBack(&list2, 8);

print(list2);

 

//7.合并两个有序链表,合并后依然有序

p = MergeList(list1, list2);

print(p);

}

void test7()

{

SListNode *ppFirst;

SListNode *p;

// 初始化

SListInit(&ppFirst);

 

 

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

SListPushBack(&ppFirst, 7);

print(ppFirst);

 

//查找链表的中间节点,只能遍历一次

p = FindMidNode(ppFirst);

printf("%d\n", p->data);

}

 

void test8()

{

SListNode *ppFirst;

SListNode *p;

// 初始化

SListInit(&ppFirst);

 

 

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

SListPushBack(&ppFirst, 7);

SListPushBack(&ppFirst, 8);

print(ppFirst);

 

//查找链表的倒数第k个节点,只能遍历一次

p = FindKNode(ppFirst,2);

if (p)

{

printf("倒数第k个节点是:%d\n", p->data);

}

else

printf("倒数第k个节点不存在\n");

}

 

 

void test9()

{

SListNode *ppFirst;

SListNode *p,*tail,*meet;

// 初始化

SListInit(&ppFirst);

 

 

// 尾部插入

SListPushBack(&ppFirst, 3);

SListPushBack(&ppFirst, 4);

SListPushBack(&ppFirst, 5);

SListPushBack(&ppFirst, 6);

SListPushBack(&ppFirst, 7);

//构成环

p = SListFind(ppFirst, 5);

tail = SListFind(ppFirst, 7);

tail->pNext = p;

 

//10.找环入口节点

meet = IsCycle(ppFirst);

printf("入口点为:%d\n", GetEntry(ppFirst, meet)->data);

 

}

 

 

void test10()

{

 

SListNode *p,*p1;

SListNode *list1 = NULL;

SListNode *list2 = NULL;

 

// 尾部插入

SListPushBack(&list1, 1);

SListPushBack(&list1, 3);

SListPushBack(&list1, 7);

SListPushBack(&list1,9);

print(list1);

 

 

SListPushBack(&list2, 2);

SListPushBack(&list2, 4);

SListPushBack(&list2, 6);

SListPushBack(&list2, 7);

SListPushBack(&list2, 8);

SListPushBack(&list2, 10);

print(list2);

p = SListFind(list1, 9);

p1 = SListFind(list1,8);

p->pNext = p1;

//11.判断是否相交,不带环

printf("%d\n", GetNode(list1, list2)->data);

}

 

猜你喜欢

转载自blog.csdn.net/sd116460/article/details/80954144