链表的基本操作

        最近为了找实习,在学数据结构,都没怎么刷面试题了,记录一下这段时间以来学过的代码吧,有需要的可以参考一下。有什么不对的地方,望指正,也可以在评论区一起互动起来!!!

代码是参考看了一些厉害的博主才完善的,要是有侵权的,请联系我及时删除!!

        学习链表的时候,链表分为很多种:单向链表,单向带头,单向循环,单向带头循环,双向链表,双向带头,双向循环,双向带头循环。比较常用的是 单向链表 双向带头循环这里我以这两个为主。

单向链表

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>



// 创建结构体
typedef struct student{
    int age;
    char name[10];
}Student;

typedef struct Node{
    Student std;
    struct Node *next;
}Node;


// 动态申请一个节点
Node * BuyNode(Student s)
{
    Node *n=(Node*)malloc(sizeof(Node));
    if(n==NULL){
        printf("malloc error!\n");
        exit(-1);
    }
    n->std=s;
    n->next=NULL;
}


// 单链表打印
void printfList(Node * plist)
{
    while(plist){
        printf("%d,%s\n", plist->std.age,plist->std.name);
        plist=plist->next;
    }
    printf("--------------------\n");
}



// 单链表尾插
void NodeInsertBack(Node ** plist,Student s)
{
    if(*plist==NULL){
        *plist=BuyNode(s);
        return;
    }
    Node * tail=*plist;
    while(tail->next){
        tail=tail->next;
    }
    tail->next=BuyNode(s);
}



// 单链表尾删
void NodeDeleteBack(Node ** plist)
{
    if(*plist==NULL){
        return;
    }
    Node * tail=*plist;
    while(tail->next->next){
        tail = tail->next;
    }
    free(tail->next);
    tail->next=NULL;
}



// 单链表的头插
void NodeInsertFront(Node ** plist,Student s)
{
    if(*plist==NULL){
        return;
    }
    Node *newNode=BuyNode(s);
    newNode->next=*plist;
    *plist = newNode;
}

// 单链表头删
void NodeDeleteFront(Node ** plist)
{
    if(*plist==NULL){
        return;
    }
    Node * delNode= *plist;
    (*plist)= delNode->next;
    free(delNode);
}



// 单链表查找
Node * NodeFind(Node * plist,Student s)
{
    if(plist==NULL){
        return NULL;
    }
    while(plist){
        if((plist->std.age == s.age) && !strcmp(plist->std.name,s.name)){
            return plist;
        }
        plist = plist->next;
    }

    return NULL;
}



// 单链表在pos位置之后插入x
void NodeInsertAfter(Node * pos,Student s)
{
    if(pos==NULL){
        return;
    }
    Node *newNode = BuyNode(s);
    newNode->next= pos->next;
    pos->next = newNode;
}


// 单链表删除pos位置之后的值
void NodeEraseAfter(Node* pos)
{
     if(pos==NULL){
        return;
    }
    Node *delNode = pos->next;
    pos->next = delNode->next;
    free(delNode);
}


//链表销毁
void NodeListDestroy(Node ** plist)
{
    if(*plist==NULL){
        return;
    }
    while(*plist){
        Node *freeNode = (*plist)->next;
        free(*plist);
        *plist = freeNode;
    }
    *plist=NULL;
}



int main(void)
{
    Student s1={.age=10,.name="111"};
    Student s2={.age=20,.name="222"};
    Student s5={.age=100,.name="breeze"};
    Student arr[] = {s1,s2,{.age=30,.name="333"},{.age=40,.name="444"},s5};
    int i;

    Node *myNode =NULL;

    NodeInsertBack(&myNode,arr[0]);//创建含有效数据的首节点
    for(i=1;i<5;i++)
        NodeInsertBack(&myNode,arr[i]);
    
    printfList(myNode);//遍历

    NodeDeleteFront(&myNode); //头删
    // NodeDeleteBack(&myNode); //尾删

    printfList(myNode);//遍历


    Node *findNode = NodeFind(myNode,s5);
    if(findNode!=NULL)
        printf("%d,%s\n", findNode->std.age,findNode->std.name);
    else
        printf("no find node\n");
    
    printf("%s cycle\n",hasCycle(myNode)?"has":"no has");

    NodeListDestroy(&myNode);

    return 0;
}


我这里还想解释一下,操作链表时,什么时候用单指针,什么时候用双指针的,但是不太会组织语言,来时留着后面再完善吧。

双向带头循环

ListNode.h文件
//ListNode.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
 

typedef int LTDataType;

typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;
 
 
//判断链表为空
bool ListEmpty(ListNode* phead);

//创建新节点
ListNode* BuyListNode(LTDataType x);

// 创建返回链表的头结点.
ListNode* ListCreate();

// 双向链表销毁
void ListDestory(ListNode* pHead);

// 双向链表打印
void ListPrint(ListNode* pHead);

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);

// 双向链表尾删
void ListPopBack(ListNode* pHead);

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);

// 双向链表头删
void ListPopFront(ListNode* pHead);

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

ListNode.c

//ListNode.c

#include "./Inc/ListNode.h"
 
ListNode* ListCreate()
{
	ListNode* phead = BuyListNode(-1);
	phead->_next = phead;
	phead->_prev = phead;
 
	return phead;
}
 
ListNode* BuyListNode(LTDataType x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
 
	if (newNode == NULL)
	{
		perror("BuyListNode    malloc");
		exit(-1);
	}
 
	newNode->_data = x;
	newNode->_next = NULL;
	newNode->_prev = NULL;
 
	return newNode;
}
 
bool ListEmpty(ListNode* phead)
{
	assert(phead);
 
	return phead->_next == phead;
}
 
void ListPrint(ListNode* pHead)
{
	assert(pHead);
	assert(!ListEmpty(pHead));
 
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		printf("%d  ", cur->_data);
		cur = cur->_next;
	}
 
}
 
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
 
	ListNode* prev = pos->_prev;
	ListNode* newNode = BuyListNode(x);
 
	prev->_next = newNode;
	newNode->_prev = prev;
	newNode->_next = pos;
	pos->_prev = newNode;
 
}
//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);
 
	ListInsert(pHead, x);
}
 
//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);
 
	ListInsert(pHead->_next, x);
}
 
void ListErase(ListNode* pos)
{
	assert(pos);
	assert(!ListEmpty(pos));
 
	ListNode* prev = pos->_prev;
	ListNode* next = pos->_next;
 
	free(pos);
 
	prev->_next = next;
	next->_prev = prev;
 
}
//尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
 
	ListErase(pHead->_prev);
}
 
//头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
 
	ListErase(pHead->_next);
}
 
 
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
 
	ListNode* cur = pHead->_next;
 
	while (cur!=pHead)
	{
		if (cur->_data == x)
		{
			return cur;
		}
 
		cur = cur->_next;
	}
	return NULL;
}
 
 
void ListDestory(ListNode* pHead)
{
	assert(pHead);
 
	ListNode* cur = pHead->_next;
 
	while (cur != pHead)
	{
 
		ListNode* next = cur->_next;
		free(cur);
		cur = NULL;
		cur = next;
	}
}

main函数测试:

#include <stdio.h>
#include "./Inc/ListNode.h"


int main(void)
{
    int arr[]={1,2,3,4,5,6,7,8,9};

    ListNode * myList=ListCreate();

    for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
        ListPushBack(myList,arr[i]);

    ListPrint(myList);

    ListPopFront(myList);

    ListNode *findNode=ListFind(myList,5);
    if(findNode!=NULL){
        printf("\nfind Node is %d\n",findNode->_data);
    }else{
        printf("no find Node");
    }
    

    ListDestory(findNode);

    return 0;
}

附加内容:

        在面试 \ 笔试中,在循环链表方面,很经常会问到如何判断链表是否有环,解决的方法可以是用快慢指针的方法,代码如下:

bool hasCycle(Node *head) 
{
    Node* slow=head;
    Node* fast=head;
 
    while(fast!=NULL)
    {
        slow=slow->next;
        fast=fast->next;
 
        if(fast==NULL)
        {
            return false;
        }
        else
        {
            fast=fast->next;
        }
 
        if(slow==fast)
        {
            return true;
        }
    }
 
    return false;   
}

        最要是核心思想就是,设定两个指针,都从头开始发。一个每次走两步,一个每次走一步,一开始肯定是距离越拉越大。但是如果都遇到环形后,两个指针的距离就不会拉大了。由于一个每次走两步,一个每次走一步,距离是以 1 的倍数进行拉近距离的。而他们的距离也是 1 的倍数,肯定会相遇的,那此时就说明,链表有环。

        如果等快指针都走到尾指针了,都没有相遇,那就说明没有环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值