链表c实现——附相关面试题

原创 2018年04月16日 09:34:00

链表c实现——附相关面试题

写链表全部需要注意的点

  1. typedef 把链表结点指针类型重命名,pNode是结构体指针类型
  2. 在每一个方法里首先要处理非法数据,传入指针为空就可能造成崩溃
  3. 删除结点相关的方法里,要提前保存要删除的结点,防止内存泄漏,防止野指针
  4. 如果牵扯到可能改变头结点指向,就得传入二级指针pNode*,或者返回值为pNode
  5. 写方法时首先考虑空链表,其次是一般情况,还要考虑只有头结点的
  6. 如果有些时候最后一次判断条件很别扭,比如反转链表三指针,尝试改变语句顺序
  7. 小技巧,找最后一个结点条件用p->next != NULL, 而其他一般是p !=NULL
  8. 灵活!一个指针不够就加指针,ppre上一个,pnxt下一个,pdel要删除的
  9. 一些面试题会用到两个指针,一快一慢,组合起来能完成很多任务

github地址

https://github.com/zzaiyuyu/linkList/tree/master/linkList

linkList.h

#pragma once
typedef int DataType;
//重命名pNode为指向结构体指针的类型
typedef struct ListNode {
    struct ListNode * pNext;
    DataType data;
}Node, *pNode;

//初始化
void SListInit(pNode* pHead);
//尾插
void linkListPushBack(pNode * pHead, int data);
//尾删
void linkListPopBack(pNode * pHead);
//头插
void linkListPushFront(pNode * pHead, int data);
//头删
void linkListPopFront(pNode * pHead);
//任意位置插入
void linkListInsert(pNode *pHead, int data, pNode pos);
//任意位置删除
void linkListErase(pNode *pHead, pNode pos);
//寻找值为data的结点
pNode findNode(pNode pHead, int data);
//返回链表长度
int SListSize(pNode pHead);
// 判断链表是否为空 
int SListEmpty(pNode pHead);
// 销毁链表 
void SListDestroy(pNode* pHead);

/******************************面试题**************************************/
void reverse_print(pNode pHead);//从尾到头打印链表

void erase_node(pNode *pHead, pNode pos);//O(1)删除结点

void insert_node(pNode *pHead, int data, pNode pos);//O(1)插入结点

int joseph_circle(pNode *pHead, int num);//单链表约瑟夫环,返回最后一个剩下的结点值

void reverse_llist(pNode *pHead);//反转链表——三指针

pNode ReverseSListOP(pNode pHead);    //反转链表--头插法 

void bubble_llist(pNode  pHead); // 冒泡排序链表

pNode merge_llist(pNode pa, pNode pb);//合并有序链表

pNode FindMiddleNode(pNode pHead);// 查找链表的中间结点,要求只能遍历一次链表 

pNode FindLastKNode(pNode pHead, int K);// 查找链表的倒数第K个结点,要求只能遍历一次链表 

pNode DeleteLastKNode(pNode pHead, int K);// 删除链表的倒数第K个结点,要求只能遍历一次链表 

int IsSListCross(pNode pHead1, pNode pHead2);// 判断两个单链表是否相交---链表不带环 

pNode GetCorssNode(pNode pHead1, pNode pHead2);// 求两个单链表相交的交点---链表不带环 

linkList.c 链表基本操作

#include "linkList.h"
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
//错误的创建新节点方式
//Node buyNewNode(int data)     
//{
//  Node newNode;               
//  newNode.data = data;
//  newNode.pNext = NULL;
//  return newNode;
//}

void SListInit(pNode* pHead)
{
    assert(pHead);
    *pHead = NULL;
}
//创建新节点
pNode buyNode(int data)
{
    pNode pNew = NULL;
    pNew = (pNode)malloc(sizeof(Node));
    if (pNew == NULL) {
        printf("分配结点失败");
        return NULL;
    }
    pNew->data = data;
    pNew->pNext = NULL;
    return pNew;
}

void linkListPushBack(pNode * pHead, int data)
{
    //保证链表存在
    assert(pHead);
    pNode pCur = NULL;
    //两种情况,空链表和非空
    if (NULL == *pHead) {
        *pHead = buyNode(data);
    }
    else {
        pCur = *pHead;
        while (NULL != (pCur)->pNext ) {
            pCur = pCur->pNext;
        }
        pCur->pNext = buyNode(data);
    }
}
void linkListPopBack(pNode * pHead) 
{
    assert(pHead);
    if (NULL == *pHead) {
        return;
    }
    pNode pPre = NULL;
    pNode pDel = NULL;
    pDel = *pHead;
    //只有头节点和有1个以上的结点
    if (NULL == pDel->pNext) {
        free(*pHead);
        *pHead = NULL;
    }
    else {
        while (NULL != pDel->pNext) {
            pPre = pDel;
            pDel = pDel->pNext;
        }
        free(pDel);
        pPre->pNext = NULL;
    }
}

void linkListPushFront(pNode * pHead, int data)
{
    assert(pHead);
    if (NULL == *pHead) {
        *pHead = buyNode(data);
    }
    else {
        pNode pNew = buyNode(data);
        pNew->pNext = *pHead;
        (*pHead)= pNew;
    }
}

void linkListPopFront(pNode * pHead)
{
    assert(pHead);
    if(NULL == *pHead){
        return;
    }
    else {
        pNode pDel = *pHead;
        (*pHead) = (*pHead)->pNext;
        free(pDel);
    }
}

void linkListInsert(pNode *pHead, int data, pNode pos)
{
    assert(pHead);
    if (NULL == *pHead || NULL == pos) {
        return;
    }
    //两种情况,头插或者任意位置插入
    if ((*pHead == pos)) {
        pNode pNew = buyNode(data);
        pNew->pNext = *pHead;
        *pHead = pNew;
    }
    else {
        pNode pCur = *pHead;
        pNode pPre = NULL;
        while (pCur && pCur != pos) {
            pPre = pCur;
            pCur = pCur->pNext;
        }
        //找到和没找到pos两种情况
        if (pCur) {
            pNode pNew = buyNode(data);
            pNew->pNext = pCur;
            pPre->pNext = pNew;
        }
    }

}

void linkListErase(pNode *pHead,pNode pos)
{
    assert(pHead);
    if (NULL == *pHead || NULL == pos) {
        return;
    }
    if (*pHead == pos) {
        pNode pDel = *pHead;
        *pHead = (*pHead)->pNext;
        free(pDel);
    }
    else {
        pNode pPre = NULL;
        pNode pCur = *pHead;
        while (pCur && pCur != pos) {
            pPre = pCur;
            pCur = pCur->pNext;
        }
        if(pCur){
            pPre->pNext = pCur->pNext;
            free(pCur);
        }

    }
}

pNode findNode(pNode pHead, int data)
{
    if (NULL == pHead) {
        return NULL;
    }
    while (pHead) {
        if (pHead->data == data) {
            return pHead;
        }
        pHead = pHead->pNext;
    }
    return NULL;
}

int SListSize(pNode pHead)
{
    if (NULL == pHead) {
        return 0;
    }
    int count = 0;
    while (pHead) {
        pHead = pHead->pNext;
        count++;
    }
    return count;
}

int SListEmpty(pNode pHead)
{
    return pHead ? 1 : 0;
}

void SListDestroy(pNode* pHead)
{
    assert(pHead);
    if (NULL == *pHead) {
        return;
    }
    pNode pDel = NULL;
    //因为最后*pHead一定是NULL,不存在野指针
    while (*pHead) {
        pDel = *pHead;
        *pHead = (*pHead)->pNext;
        free(pDel);
    }
}

interview.c 链表相关面试题

#include "linkList.h"
#include <stdio.h>
#include <assert.h>
//从尾到头打印链表
void reverse_print(pNode pHead)
{
    if (NULL == pHead) {
        return;
    }
    reverse_print(pHead->pNext);
    printf("%d", pHead->data);
}
//O(1)删除非尾结点
void erase_node(pNode *pHead, pNode pos)
{
    assert(pHead);
    if (NULL == *pHead || NULL == pos) {
        return;
    }
    //分为头删和删除非尾结点两种情况
    if (pos == *pHead) {
        pNode pDel = *pHead;
        *pHead = (*pHead)->pNext;
        free(pDel);
    }
    else {
        pNode pDel = pos;
        pDel = pDel->pNext;
        if (pDel->pNext) {
            pos->data = pDel->data;
            pos->pNext = pDel->pNext;
            free(pDel);
        }
    }
}
//O(1)插入结点
void insert_node(pNode *pHead, int data, pNode pos)
{
    assert(pHead);
    if (NULL == *pHead || NULL == pos) {
        return;
    }
    pNode pNew = NULL;
    pNew = (pNode)malloc(sizeof(Node));
    if (NULL == pNew) {
        return;
    }
    pNew->data = pos->data;
    pNew->pNext = pos->pNext;
    pos->data = data;
    pos->pNext = pNew;
}
//单链表约瑟夫环,返回最后一个剩下的结点值
int joseph_circle(pNode *pHead, int num)
{
    assert(pHead);
    if (NULL == *pHead || num <= 1) {
        return -1;
    }
    pNode pStr = *pHead;
    pNode pCur = NULL;
    pNode pDel = NULL;  //需要被剔除的结点
    while (pStr->pNext != pStr) {
        //报数
        pCur = pStr;
        int count = num;
        while (--count) {
            pCur = pCur->pNext;
        }
        //剔除
        pDel = pCur;
        pCur = pCur->pNext;
        pStr->pNext = pCur;
        pStr = pCur;
        free(pDel);
    }
    return pStr->data;
}
//反转链表——三指针
void reverse_llist(pNode *pHead) {
    assert(pHead);
    if (NULL == *pHead) {
        return;
    }
    pNode pPre = NULL;
    pNode pCur = *pHead;
    pNode pNxt = pCur;
    while (pCur) {              //这个题判断比较精妙,如果以pNxt为条件不妥
        pNxt = pCur->pNext;     //这句放在pcur改变之前!
        pCur->pNext = pPre;     //当觉得最后一次判断条件比较复杂时,尝试调整语句顺序和判断条件
        pPre = pCur;
        pCur = pNxt;

    }
    *pHead = pPre;
}
//反转链表--头插法
pNode ReverseSListOP(pNode pHead)
{
    if (NULL == pHead) {
        return NULL;
    }
    pNode pNew = NULL;
    pNode pNxt = NULL;
    //处理第一个结点
    pNew = pHead;
    pHead = pHead->pNext;
    pNew->pNext = NULL;     //这两步顺序很关键,新的第一个头结点要指空,
                            //因为是头插,到后面不会再改动这个
    while (pHead) {
        pNxt = pHead->pNext;
        pHead->pNext = pNew;
        pNew = pHead;
        pHead = pNxt;
    }
    return pNew;
}
//冒泡排序链表
void bubble_llist(pNode pHead) {
    if (NULL == pHead) {
        return;
    }
    //找到尾结点
    pNode pTail = pHead;
    while (pTail->pNext) {
        pTail = pTail->pNext;
    }
    pNode pCur = NULL;
    pNode pPre = NULL;
    //当ptail指向头说明只有一个节点了,不需要比较
    while (pTail != pHead) {
        pCur = pHead;
        //每趟比到pcur在ptail前就可以了,因为pcur与pcur->next值比较
        while (pCur != pTail) {
            if ((pCur->data) > (pCur->pNext->data)) {
                int tmp;
                tmp = pCur->data;
                pCur->data = pCur->pNext->data;
                pCur->pNext->data = tmp;
            }
            pPre = pCur;        
            pCur = pCur->pNext;
        }
        pTail = pPre;       //ptail每次后退一个位置
    }
}
//合并两个有序链表
pNode merge_llist(pNode pa, pNode pb)
{
    pNode pNew = NULL;
    //处理头结点
    if (pa && (pa->data <= pb->data)) {
        pNew = pa;
        pa = pa->pNext;
    }
    else if(pb){
        pNew = pb;
        pb = pb->pNext;
    }
    pNode pNewHead = pNew;
    while (pa && pb) {
        if (pa->data <= pb->data) {
            pNew->pNext = pa;
            pa = pa->pNext;
        }
        else {
            pNew->pNext = pb;
            pb = pb->pNext;
        }
        pNew = pNew->pNext;
    }
    //走到这里,pa或者pb为空
    if (pa) {
        pNew->pNext = pa;
    }
    else {
        pNew->pNext = pb;
    }
    return pNewHead;
}
//查找链表的中间结点
pNode FindMiddleNode(pNode pHead)
{
    if (NULL == pHead) {
        return NULL;
    }
    //考虑两种情况,奇数个和偶数个结点
    pNode pLow = pHead;
    pNode pFst = pHead;
    pNode pPre = NULL;  //如果偶数个结点,返回较小的
    while (pFst && pFst->pNext) {
        pPre = pLow;
        pLow = pLow->pNext;
        pFst = pFst->pNext->pNext;
    }
    //走到这里pfst如果是空就是偶数个结点
    if (NULL == pFst) {
        return pPre;
    }
    else {
        return pLow;
    }
}
//倒数第K个结点
pNode FindLastKNode(pNode pHead, int K)
{
    if (NULL == pHead || K <= 0) {
        return NULL;
    }
    pNode pFst = pHead;
    while (K--) {
        if (NULL == pFst) { //这个条件一定在走fst之前
            return NULL;        //一是保证即使k很大也能退出,二保证pFst=NULL的情况正常出去
        }
        pFst = pFst->pNext;
    }
    pNode pLow = pHead;
    while (pFst) {
        pFst = pFst->pNext;
        pLow = pLow->pNext;
    }
    return pLow;
}
// 删除链表的倒数第K个结点
pNode DeleteLastKNode(pNode pHead, int K)
{
    if (NULL == pHead || K <= 0) {
        return NULL;
    }
    pNode pFst = pHead;
    while (K--) {
        if (NULL == pFst) { //这个条件一定在走fst之前
            return NULL;        //一是保证即使k很大也能退出,二保证pFst=NULL的情况正常出去
        }
        pFst = pFst->pNext;
    }
    pNode pLow = pHead;
    pNode pPre = NULL;
    while (pFst) {
        pPre = pLow;
        pFst = pFst->pNext;
        pLow = pLow->pNext;
    }
    //这里保证找到了倒数第K个结点pLow,考虑是不是头结点
    if (pHead == pLow) {
        pNode pNew = pHead->pNext;
        free(pHead);
        return pNew;
    }
    else {
        pPre->pNext = pLow->pNext;
        free(pLow);                 //如果在别的程序使用plow存在内存泄漏?
        return pHead;
    }
}
// 判断两个单链表是否相交---链表不带环 
int IsSListCross(pNode pHead1, pNode pHead2)
{
    if (NULL == pHead1 || NULL == pHead2) {
        return -1;
    }
    //找两个链表的最后一个结点
    while (pHead1->pNext) {
        pHead1 = pHead1->pNext;
    }
    while (pHead2->pNext) {
        pHead2 = pHead2->pNext;
    }
    if (pHead1 == pHead2) {
        return 1;
    }
    else {
        return -1;
    }
}
//求两个单链表相交的交点--- 链表不带环
pNode GetCorssNode(pNode pHead1, pNode pHead2)
{
    if (NULL == pHead1 || NULL == pHead2) {
        return NULL;
    }
    int len1 = 0;
    int len2 = 0;
    pNode pCur1 = pHead1;
    pNode pCur2 = pHead2;
    while (pCur1) {
        len1++;
        pCur1 = pCur1->pNext;
    }
    while (pCur2) {
        len2++;
        pCur2 = pCur2->pNext;
    }
    int diff = len1 - len2;
    while (0 != diff) {
        if (diff > 0) {
            pHead1 = pHead1->pNext;
            diff--;
        }
        else {
            pHead2 = pHead2->pNext;
            diff++;
        }
    }
    //走到这里两个指针距相交点有相同距离
    while (pHead1 != pHead2) {
        pHead1 = pHead1->pNext;
        pHead2 = pHead2->pNext;
    }
    return pHead1;
}

test.c

#include "linkList.h"
#include <stdio.h>
void test_interview(pNode *pHead);
int main()
{
    //所有测试函数传入的头指针
    pNode pHead = NULL;
    //linkListPushBack(&pHead, 5);
    //linkListPushBack(&pHead, 4);
    //linkListPushBack(&pHead, 3);
    //linkListPopBack(&pHead);
    //linkListPopBack(&pHead);
    //linkListPopBack(&pHead);
    //linkListPushFront(&pHead, 10);
    //linkListPushFront(&pHead, 9);
    //pNode tmp10 = findNode(pHead, 10);
    //pNode tmp9 = findNode(pHead, 9);
    //linkListInsert(&pHead, 7, tmp9);
    //linkListInsert(&pHead, 7, tmp10);
    //linkListErase(&pHead, findNode(pHead, 7));
    //linkListErase(&pHead, findNode(pHead, 7));
    //linkListPopFront(&pHead);
    //linkListPopFront(&pHead);
    //int ret = SListEmpty(pHead);
    //int tmp = SListSize(pHead);
    //SListDestroy(&pHead);

    test_interview(&pHead); 
}
//测试面试题函数
void test_interview(pNode *pHead)
{
    //在这个函数里,pHead是指向头指针的指针,*pHead是头指针
    linkListPushBack(pHead, 2);
    linkListPushBack(pHead, 4);
    linkListPushBack(pHead, 7);
    linkListPushBack(pHead, 3);
    linkListPushBack(pHead, 5);

    //链表倒序打印
    //pNode pNow = *pHead;
    //reverse_print(*pNow);

    //O(1)删除结点
    //erase_node(pHead, findNode(*pHead, 3));

    //O(1)插入结点
    //insert_node(pHead, 88, findNode(*pHead, 5));

    //约瑟夫
    //pNode pTail = findNode(pHead, 1);
    //pTail->pNext = *pHead;
    //int ret = joseph_circle(pHead, 1);

    //反转——三指针
    //reverse_llist(pHead);

    //反转——头插
    //pNode pNew = ReverseSListOP(*pHead);

    //冒泡
    //bubble_llist(pHead);

    //合并有序
    pNode pb = NULL;
    linkListPushBack(&pb, 1);
    linkListPushBack(&pb, 3);
    linkListPushBack(&pb, 5);
    pNode pNew = merge_llist(*pHead, pb);

    //查找中间结点
    //pNode pMid = FindMiddleNode(*pHead);

    //倒数第k个
    //pNode pLastK = FindLastKNode(*pHead, 6);

    //删除倒数第k个
    //pNode pNew = DeleteLastKNode(*pHead, 2);

    //判断是否相交
    //pNode pTail = findNode(pb, 5);
    //pTail->pNext = findNode(*pHead, 7);
    //int ret = IsSListCross(*pHead, pb);
    //pNode ret = GetCorssNode(*pHead, pb);

}

链表常见面试题-C语言实现

前面的博客已经介绍过了实现链表的一些相关的功能,所以在这里就不对链表多加解释说明了。 对于链表相关面试题这里解释几个有特点的题: 1.对于求查找链表的中间结点,要求只能遍历一次链表? 方式:使用两个指...
  • IT_iverson
  • IT_iverson
  • 2017-08-03 14:03:37
  • 1798

C语言实现单链表面试题-------进阶篇

在基础篇中,简单的描述了C语言单链表面试中的常见简单题型,这里将在上一篇的基础上描述 讨论链表是否带环与两链表是否相交问题 复杂链表的复制...
  • smile_zhangw
  • smile_zhangw
  • 2017-12-09 15:05:10
  • 95

面试考题之9.2:链表(C/C++版)

2.1 编写代码,移除未排序链表中的重复结点。进阶:如果不得使用临时缓冲区,该怎么解决? 解决方案: 方案1: 使用散列表 暂略 方案2:不借助额外缓冲区 1 2 3 4 5 6 7 8 ...
  • JohnnyHu90
  • JohnnyHu90
  • 2015-06-07 16:35:22
  • 2754

常见的链表面试题大汇总:

常见的链表面试题大汇总: 源代码下载链接 1,创建一个链表结点 2,遍历链表中的所有结点 3,倒序打印链表 4,往链表末尾添加结点 5,往链表前端添加结点,6,获取链表的节点数目 7,销...
  • mengni123321
  • mengni123321
  • 2015-09-05 20:57:12
  • 512

链表面试题总结(一)

基于上一次写的链表,现在,我们来讨论下面这些问题。1.链表的冒泡排序 2.删除无头非尾节点 3.反转链表 4.在当前节点前插入一个数据x 5.查找链表的中间节点。 6.删除单链表的倒数第K个...
  • qq_26768741
  • qq_26768741
  • 2016-06-11 12:06:14
  • 5478

链表面试笔试题目总结

链表是最基本的数据结构,凡是学计算机的必须的掌握的,在面试的时候经常被问到,关于链表的实现,百度一下就知道了。在此可以讨论一下与链表相关的练习题。 (1)在单链表上插入一个元素,要求时间复杂度为...
  • ywok526
  • ywok526
  • 2014-08-30 22:04:33
  • 960

C/C++面试题之链表

占位
  • qq_31029351
  • qq_31029351
  • 2017-07-10 22:23:55
  • 70

【数据结构】链表的原理及与其相关的常见面试题总结

链表是一种数据结构,和数组同级。比如,Java中我们使用的ArrayList,其实现原理是数组。而LinkedList的实现原理就是链表了。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。下面...
  • jianyuerensheng
  • jianyuerensheng
  • 2016-06-06 09:49:49
  • 1765

C语言实现单链表面试题汇总

这篇博客只有针对单链表的不同面试题的不同函数,没有对单链表的具体实现方法的介绍。 单链表的具体实现方法(创建,初始化,前插,后插,删除,插入,销毁等),可以参考我的另一边博客: http://bl...
  • ljx_5489464
  • ljx_5489464
  • 2016-03-20 03:51:53
  • 1815

c语言链表以及面试题

#ifndef __PNODE_H__ //不带头结点的链表 #define __PNODE_H__ #include #include #include typedef int D...
  • turn__back
  • turn__back
  • 2016-12-07 23:39:13
  • 893
收藏助手
不良信息举报
您举报文章:链表c实现——附相关面试题
举报原因:
原因补充:

(最多只允许输入30个字)