最近为了找实习,在学数据结构,都没怎么刷面试题了,记录一下这段时间以来学过的代码吧,有需要的可以参考一下。有什么不对的地方,望指正,也可以在评论区一起互动起来!!!
代码是参考看了一些厉害的博主才完善的,要是有侵权的,请联系我及时删除!!
学习链表的时候,链表分为很多种:单向链表,单向带头,单向循环,单向带头循环,双向链表,双向带头,双向循环,双向带头循环。比较常用的是 单向链表 和 双向带头循环,这里我以这两个为主。
单向链表
#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 的倍数,肯定会相遇的,那此时就说明,链表有环。
如果等快指针都走到尾指针了,都没有相遇,那就说明没有环。