漫鱼

计科专业大学生

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

链表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);

}
阅读更多
个人分类: 算法/数据结构
上一篇vim,gdb,makefile
下一篇进程创建和替换——fork和exec
想对作者说点什么? 我来说一句

常见的链表面试题大汇总

2015年09月05日 2.49MB 下载

没有更多推荐了,返回首页

关闭
关闭