Linked list c implementation - with relevant interview questions

Linked list c implementation - with relevant interview questions

All the points that need to be paid attention to when writing a linked list

  1. typedef renames the linked list node pointer type, pNode is a structure pointer type
  2. In each method, the illegal data must be processed first, and the incoming pointer is null, which may cause a crash
  3. In the methods related to deleting nodes, the nodes to be deleted should be saved in advance to prevent memory leaks and wild pointers
  4. If it involves the possibility of changing the point of the head node, you have to pass in the secondary pointer pNode*, or the return value is pNode
  5. When writing a method, consider the empty linked list first, followed by the general case, and consider only the head node
  6. If sometimes the last judgment condition is awkward, such as reversing the three pointers of the linked list, try to change the statement order
  7. A little trick, use p->next != NULL to find the last node condition, while the others are generally p !=NULL
  8. flexible! If a pointer is not enough, add a pointer, ppre is the previous one, pnxt is the next one, and the pdel is to be deleted.
  9. Some interview questions will use two pointers, one fast and one slow, which can be combined to complete many tasks

github address

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 Basic operation of linked list

#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 linked list related interview questions

#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);

}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325899340&siteId=291194637