精华内容
参与话题
问答
  • 数据结构-单链表基本操作实现(含全部代码)

    万次阅读 多人点赞 2018-09-14 12:08:02
    今天是单链表的实现,主要实现函数如下: InitList(LinkList &L) 参数:单链表L 功能:初始化 时间复杂度 O(1) ListLength(LinkList L) 参数:单链表L 功能:获得单链表长度 时间复杂度O(n) ...

    今天是单链表的实现,主要实现函数如下:

    •     InitList(LinkList &L)               参数:单链表L 功能:初始化 时间复杂度 O(1)
    •     ListLength(LinkList L)           参数:单链表L 功能:获得单链表长度 时间复杂度O(n)
    •     ListInsert(LinkList &L,int i,ElemType e) 参数:单链表L,位置i,元素e 功能:位置i后插 时间复杂度O(n)[加入了查找]
    •                                                                         若已知指针p指向的后插 O(1)
    •     ListDelete(LinkList &L,int i) 参数:单链表L,位置i 功能:删除位置i元素 时间复杂度O(n)[加入了查找]
    •                                                     若已知p指针指向的删除 最好是O(1),因为可以与后继结点交换数据域,然后删除后继结点。
    •                                                     最坏是O(n),即从头查找p之前的结点,然后删除p所指结点
    •     LocateElem(LinkList L,ElemType e) 参数:单链表L,元素e 功能:查找第一个等于e的元素,返回指针 时间复杂度O(n)

    代码:

    /*
        Project: single linkeed list (数据结构 单链表)
        Date:    2018/09/14
        Author:  Frank Yu
    	InitList(LinkList &L) 参数:单链表L 功能:初始化 时间复杂度 O(1)
    	ListLength(LinkList L) 参数:单链表L 功能:获得单链表长度 时间复杂度O(n) 
    	ListInsert(LinkList &L,int i,ElemType e) 参数:单链表L,位置i,元素e 功能:位置i后插 时间复杂度O(n)[加入了查找]
    	                                        若已知指针p指向的后插 O(1)
    	ListDelete(LinkList &L,int i) 参数:单链表L,位置i 功能:删除位置i元素 时间复杂度O(n)[加入了查找] 
    	                              若已知p指针指向的删除 最好是O(1),因为可以与后继结点交换数据域,然后删除后继结点。
    								  最坏是O(n),即从头查找p之前的结点,然后删除p所指结点
    	LocateElem(LinkList L,ElemType e) 参数:单链表L,元素e 功能:查找第一个等于e的元素,返回指针 时间复杂度O(n) 
    */
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    using namespace std;
    #define Status int
    #define ElemType int
    //单链表结点数据结构
    typedef struct LNode
    {
    	ElemType data;//数据域
    	struct LNode *next;//指针域
    }LNode,*LinkList;
    //**************************基本操作函数***************************//
    //初始化函数
    Status InitList(LinkList &L)
    {
     L = new LNode;//生成头结点 这样删除等操作就不必分第一个结点和其他了
     L->next = NULL;
     return 1;
    }
    //获取单链表长度 头结点无数据,不算
    int ListLength(LinkList L)
    {
    	LinkList p=L;int sum=0;
    	while(p)
    	{
    	 sum++;
    	 p=p->next;
    	}
    	return sum-1;//去除头结点
    }
    //插入函数--后插法 插入到第i(1<=i<=length+1)个位置 即i-1之后 不必区分i的位置
    bool ListInsert(LinkList &L,int i,ElemType e)
    {
    	LNode* s;LinkList p=L;int j=0;
    	while(p&&(j<i-1))//j指到i-1位置或者p已经到最后时跳出
    	{
    	 p=p->next;
    	 ++j;
    	}
    	if(!p||j>i-1)//i<1或者i>ListLength(L)+1时,插入位置无效 不调用ListLength,提高效率
    	{
    		printf("插入位置无效!!!\n");
    		return false;
    	}
    	s=new LNode;
    	s->data=e;
    	s->next=p->next;
    	p->next=s;
    	return true;
    }
    //删除函数 删除位置i的结点 即删除i-1之后的结点
    bool ListDelete(LinkList &L,int i)
    {
     	LNode* s;LinkList p=L;int j=0;
    	LinkList q;
    	while(p&&(j<i-1))//j指到i-1位置
    	{
    	 p=p->next;
    	 ++j;
    	}
    	if(!(p->next)||j>i-1)//i<1或者i>ListLength(L)时,删除位置无效
    	{
    		printf("删除位置无效!!!\n");
    		return false;
    	}
    	q=p->next;
    	p->next=q->next;
    	free(q);//释放空间
    	return true;
    }
    //查找函数 按值查找 查找第一个等于e的结点 成功返回该结点指针,否则返回NULL
    LNode *LocateElem(LinkList L,ElemType e)
    {
    	LNode *p=L;
    	while(p&&(p->data!=e))
    	{
    		p=p->next;
    	}
    	return p;
    }
    //**************************功能实现函数**************************//
    //遍历输出函数
    void PrintList(LinkList L)
    {
    	LinkList p=L->next;//跳过头结点
    	if(ListLength(L))
    	{
    		printf("当前单链表所有元素:");
    		while(p)
    		{
    			printf("%d ",p->data);
    			p=p->next;
    		}
    		printf("\n");
    	}
    	else
    	{
    		printf("当前单链表已空!\n");
    	}
    }
    //插入功能函数 调用ListInsert后插
    void Insert(LinkList &L)
    {
      int place;ElemType e;bool flag;
      printf("请输入要插入的位置(从1开始)及元素:\n");
      scanf("%d%d",&place,&e);
      flag=ListInsert(L,place,e);
      if(flag) 
      {
    	printf("插入成功!!!\n");
    	PrintList(L);
      }
    }
    //删除功能函数 调用ListDelete删除
    void Delete(LinkList L)
    {
      int place;bool flag;
      printf("请输入要删除的位置(从1开始):\n");
      scanf("%d",&place);
      flag=ListDelete(L,place);
      if(flag) 
      {
    	printf("删除成功!!!\n");
    	PrintList(L);
      }
    }
    //查找功能函数 调用LocateElem查找
    void Search(LinkList L)
    {
      ElemType e;LNode *q;
      printf("请输入要查找的值:\n");
      scanf("%d",&e);
      q=LocateElem(L,e);
      if(q) 
      {
    	printf("找到该元素!\n");
      }
      else
    	printf("未找到该元素!\n");
    }
    //菜单
    void menu()
    {
       printf("********1.后插    2.删除*********\n");
       printf("********3.查找    4.输出*********\n");
       printf("********5.退出          *********\n");
    }
    //主函数
    int main()
    {
     LinkList L;int choice;
     InitList(L);
     while(1)
     {
      menu();
      printf("请输入菜单序号:\n");
      scanf("%d",&choice);
      if(choice==5) break;
      switch(choice)
      {
      case 1:Insert(L);break;
      case 2:Delete(L);break;
      case 3:Search(L);break;
      case 4:PrintList(L);break;
      default:printf("输入错误!!!\n");
      }
     }
     return 0;
    }
    

    结果截图:

    后插结果截图
    删除结果截图
    查找结果截图
    输出结果截图

    更多数据结构实现:数据结构严蔚敏版的实现

    有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

    展开全文
  • 链表】单链表的基本操作详解(C语言)

    万次阅读 多人点赞 2019-06-01 22:02:15
    本文是单链表的C语言实现方法,包括创建单链表结点、创建单链表、显示链表的数据、获取链表长度、在链表指定位置插入结点、删除指定位置的结点、查找链表中指定的数据、修改链表中指定位置结点的值、修改链表中指定...

    本文是单链表的C语言实现方法,包括单链表的创建、插入、删除、修改、查找等基本操作。

    链表的底层是通过指针将一个个零散的内存块连接起来,链表的每个内存块称为结点。

    单链表结点结构体

    单链表的结点上存储数据data和下个结点的地址——后继指针next,所以可以定义一个结构体存储这两个元素:

    //结点结构体
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    

    创建结点

    //创建结点
    Node* create_node(int data)
    {
        Node* node = (Node*)malloc(sizeof(Node));//申请内存
        node->data = data;
        node->next = NULL;
        return node;
    }
    

    创建单链表

    步骤:首先需要创建一个头结点,然后头结点指向一个新结点,新的结点又指向一个新结点,我们可以通过循环来创建一个指定长度的单链表,如下所示:

    //创建长度为num的单链表
    Node* create_list(int num)
    {
        Node* head = create_node(1);//创建头结点
        Node* tmp = head;//初始化tmp指针指向头结点,表示头结点
        for (int i = 2; i <= num; ++i)
        {
            Node* node = create_node(i);//创建新结点
            tmp->next = node;//头结点的下一个结点等于新结点
            tmp = node;//tmp指针指向新结点
        }
        return head;
    }
    

    获取链表的长度

    //链表长度
    size_t list_len(Node* head)
    {
        int len = 0;
        while(head)
        {
            head = head->next;
            len++;
        }
        return len;
    }
    

    插入结点

    • 插入时需要考虑的情况:
      • 1.插入前判断插入的位置是否超出链表的范围
      • 2.插入的位置为0时,也就是插入一个头结点
    • 插入步骤,如下图:
      • 让新结点下一个结点等于当前指针指向的结点的下一个结点
      • 改变当前指针的指向
      • 当前指针指向的结点指向新结点
        在这里插入图片描述
    //在链表指定位置插入一个结点 返回头结点
    Node* list_insert(Node* head, int index, int data)
    {
    	//判断链表是否为空以及插入的位置是否超出链表的范围
        if(index > list_len(head) || index < 0)
            return head;
        else if(index == 0) //插入0位置,即插入一个头结点
        {
            Node* node = create_node(data);
            node->next = head;//新结点的下一个结点等于头结点
            head = node;//头结点等于新结点
        }
        else
        {
            Node* tmp = head;
            for(int i=0; i<index-1; i++)
            {
                tmp = tmp->next;
            }
            Node* node = create_node(data);
            node->next = tmp->next;
            tmp->next = node;
        }
        return head;
    }
    

    删除结点

    • 删除和插入一个结点方法类似,需要考虑以下几点:
      • 1.删除前判断链表是否为空,删除的位置是否超出链表的范围
      • 2.删除的位置为0时,也就是删除一个头结点
      • 3.删除的结点记得释放内存,防止野指针
    • 删除步骤,如下图:
      • 将指针移到删除结点的前一个结点node
      • 通过tmp保存要删除的结点
      • node的下一个结点等于tmp的下一个结点
      • 释放tmp的内存,并将tmp置为空,防止野指针
        在这里插入图片描述
    //删除指定位置的一个结点 返回头结点
    Node* list_delete(Node* head, int index)
    {
        if(head == NULL || index >= list_len(head) || index < 0)
            return head;
        //判断链表是否为空以及插入的位置是否超出链表的范围
        else if(index == 0)
        {
            Node* tmp = head;
            head = tmp->next;//头结点等于头结点的下一个结点
            free(tmp);//释放删除结点的内存
            tmp = NULL;
        }
        else
        {
            Node* node = head;
            for(int i=0; i<index-1; i++)
            {
                node = node->next;
            }
    
            Node* tmp = node->next;
            node->next = tmp->next;
            free(tmp);//释放删除结点的内存
            tmp = NULL;
        }
        return head;
    }
    

    查找链表中指定的数据

    • 参数:链表的头结点 指定的数据
    • 返回值:该数据对应的结点
    Node* list_find(Node* head,int data)
    {
        if(head == NULL)
            return NULL;
        Node* node = head;
        while(node)
        {
            if(node->data == data)
                return node;//返回该数据对应的结点
            node = node->next;
        }
        return NULL;//链表中不存在该数据,返回NULL
    }
    

    修改链表中指定位置结点的值

    • 参数:链表的头结点 指定的位置 新的数据
    • 返回值:该数据对应的结点
    bool modify_index(Node* head,int index, int data)
    {
        if(head == NULL || index < 0)
            return false;
        Node* node = head;
        for (int i = 0; i < index; ++i)
        {
            if (node == NULL)
                return false;
            node = node->next;
        }
        node->data =  data;
        return true;
    }
    

    修改链表中指定数据的值

    • 参数:链表的头结点 指定的数据 新的数据
    • 返回值:该数据对应的结点
    • 如果链表中有多个数据等于指定的数据,只修改第一个
    bool modify_data(Node* head, int data, int val)
    {
        Node* node = list_find(head,data);
        if(node)
        {
            node->data = val;
            return true;
        }
        return false;
    } 
    

    完整代码

    链表测试代码请前往:https://github.com/HongKnight/linked-list/tree/master/single_list

    #include <stdio.h>
    #include <malloc.h>
    #include <stdlib.h>
    #include <stdbool.h>
    
    //结点结构体
    typedef struct Node
    {
        int data;
        struct Node* next;
    } Node;
    
    //创建结点
    Node* create_node(int data)
    {
        Node* node = (Node*)malloc(sizeof(Node));//申请内存
        node->data = data;
        node->next = NULL;
    }
    
    //创建长度为num的链表
    Node* create_list(int num)
    {
        Node* head = create_node(1);//创建头结点
        Node* tmp = head;//初始化tmp指针指向头结点,也表示头结点
        for (int i = 2; i <= num; ++i)
        {
            Node* node = create_node(i);//创建新结点
            tmp->next = node;//头结点的下一个结点等于新结点
            tmp = node;//tmp指针指向新结点
        }
        return head;
    }
    
    //显示链表的数据
    void show_list(Node* head)
    {
        while(head)
        {
            printf("%d ", head->data);
            head = head->next;
        }
        printf("\n");
    }
    
    //获取链表长度
    size_t list_len(Node* head)
    {
        int len = 0;
        while(head)
        {
            head = head->next;
            len++;
        }
        return len;
    }
    
    
    //在链表指定位置插入一个结点 返回头结点
    Node* list_insert(Node* head, int index, int data)
    {
        //判断链表是否为空以及插入的位置是否超出链表的范围
        if(head == NULL || index > list_len(head) || index < 0)
            return NULL;
        //插入0位置,即插入一个头结点
        else if(index == 0)
        {
            Node* node = create_node(data);
            node->next = head;//新结点的下一个结点等于头结点
            head = node;//头结点等于新结点
        }
        else
        {
            Node* tmp = head;
            for(int i=0; i<index-1; i++)
            {
                tmp = tmp->next;
            }
            Node* node = create_node(data);
            node->next = tmp->next;
            tmp->next = node;
        }
        return head;
    }
    
    //删除指定位置的一个结点 返回头结点
    Node* list_delete(Node* head, int index)
    {
        if(head == NULL || index >= list_len(head) || index < 0)
            return NULL;
        //判断链表是否为空以及插入的位置是否超出链表的范围
        else if(index == 0)
        {
            Node* tmp = head;
            head = tmp->next;//头结点等于第二结点
            free(tmp);//释放删除结点的内存
            tmp = NULL;
        }
        else
        {
            Node* node = head;
            for(int i=0; i<index-1; i++)
            {
                node = node->next;
            }
    
            Node* tmp = node->next;
            node->next = tmp->next;
            free(tmp);//释放删除结点的内存
            tmp = NULL;
        }
        return head;
    }
    
    //查找链表中指定的数据
    Node* list_find(Node* head,int data)
    {
        if(head == NULL)
            return NULL;
        Node* node = head;
        while(node)
        {
            if(node->data == data)
                return node;//返回该数据对应的结点
            node = node->next;
        }
        return NULL;//链表中不存在该数据,返回NULL
    }
    
    //修改链表中指定位置结点的值
    bool modify_index(Node* head,int index, int data)
    {
        if(head == NULL || index < 0)
            return false;
        Node* node = head;
        for (int i = 0; i < index; ++i)
        {
            if (node == NULL)
                return false;
            node = node->next;
        }
        node->data =  data;
        return true;
    }
    
    //修改链表中指定数据的值
    bool modify_data(Node* head, int data, int val)
    {
        Node* node = list_find(head,data);
        if(node)
        {
            node->data = val;
            return true;
        }
        return false;
    } 
    

    更多链表相关代码请前往:https://github.com/HongKnight/linked-list

    展开全文
  • 链表的各种基本操作

    万次阅读 多人点赞 2018-03-27 10:21:36
    1. 链表定义。#ifndef _LISTNODE_ #define _LISTNODE_ struct ListNode { int m_value; ListNode * m_pNext; }; #endif2. 初始化链表。void InitList(ListNode * L) { L = nullptr; cout &lt;&lt; &...

    1. 链表定义。

    #ifndef _LISTNODE_
    #define _LISTNODE_
    
    struct ListNode
    {
    	int m_value;
    	ListNode * m_pNext;
    };
    
    #endif

    2. 初始化链表。

    void InitList(ListNode * L)
    {
    	L = nullptr;
    	cout << "InitList Success! " << endl;
    }

    3. 创建链表。共三种方法。

    // ------创建线性表 1: 头结点插入法,头结点不放数据------
    ListNode * CreateListFromHead()
    {
    	ListNode * pHead = new ListNode;
    	pHead->m_pNext = nullptr;
    	if (pHead == nullptr)
    	{
    		cout << "分配内存失败!" << endl;
    		return nullptr;
    	}
    
    	int input;
    	
    	while (cin>>input && input != -1)
    	{
    		ListNode *pTmp = new ListNode;
    		pTmp->m_value = input;
    		pTmp->m_pNext = pHead->m_pNext;   //将节点插入到表头,这是头插法
    		pHead->m_pNext = pTmp;
    		pTmp = nullptr;
    	}
    
    	//pTmp = nullptr;
    	return pHead;
    }
    // ------创建线性表 2: 尾插法,头结点不放数据------
    ListNode * CreateListFromTail2()
    {
    	ListNode * pHead = new ListNode;
    	if (pHead == nullptr)
    	{
    		cout << "分配内存失败!" << endl;
    		return nullptr;
    	}
    	pHead->m_pNext = nullptr;
    
    	ListNode *pTail = new ListNode;
    	pTail = pHead;
    
    	int input;
    
    	while (cin>>input && input != -1)
    	{
    		ListNode *pTmp = new ListNode;
    		pTmp->m_value = input;
    		pTail->m_pNext = pTmp;   //尾插法
    		pTail = pTmp;
    		pTmp = nullptr;
    	}
    	pTail->m_pNext = nullptr;
    
    	return pHead;
    }
    // ------创建线性表 3: 尾插法,无头结点------
    ListNode * CreateListFromTail()
    {
    	/*ListNode * pHead = new ListNode;
    	if (pHead == nullptr)
    	{
    		cout << "分配内存失败!" << endl;
    		return nullptr;
    	}*/
    	ListNode *pHead = nullptr;
    
    	ListNode *pTail = pHead;
    
    	int input;
    
    	while (cin>>input && input != -1)
    	{
    		ListNode *pTmp = new ListNode;
    		pTmp->m_value = input;
    
    		if (pHead == nullptr)
    			pHead = pTmp;
    		else
    			pTail->m_pNext = pTmp;   //尾插法
    		pTail = pTmp;
    
    		pTmp = nullptr;
    	}
    	if (pTail != nullptr)
    		pTail->m_pNext = nullptr;
    
    	return pHead;
    }

    4. 插入。共两种方法。

    // -------------------在pos位置的前方插入-----------------------------
    void InsertListNodeFromFront(ListNode *&L, int pos, int value)  //可能需要改变头结点,因此L要取&
    {
    	ListNode *tmp = new ListNode;
    	tmp->m_value = value;
    	if (L == nullptr || pos == 0)
    	{
    		tmp->m_pNext = L;
    		L = tmp;
    		return;
    	}
    
    	if ( pos >= ListLen(L) || pos < 0)
    	{
    		cout << "Error input pos."<< endl;
    		return;
    	}
    
    	ListNode *pInsert = L;
    	while (--pos)
    	{
    		pInsert = pInsert->m_pNext;
    	}
    	tmp->m_pNext = pInsert->m_pNext;
    	pInsert->m_pNext = tmp;
    
    	return;
    }
    // -----------------在pos后插入--------------------
    void InsertListNodeFromBack(ListNode *&L, int pos, int value)
    {
    	ListNode *tmp = new ListNode;
    	tmp->m_value = value;
    	if (L == nullptr || pos == 0)
    	{
    		tmp->m_pNext = L;
    		L = tmp;
    		return;
    	}
    	
    	if ( pos >= ListLen(L) || pos < 0)
    	{
    		cout << "Error input pos."<< endl;
    		return;
    	}
    
    	ListNode *pInsert = L;
    	while (pos--)
    	{
    		pInsert = pInsert->m_pNext;
    	}
    	tmp->m_pNext = pInsert->m_pNext;
    	pInsert->m_pNext = tmp;
    
    	return;
    }

    5. 删除。

    void DeleteListNode(ListNode *&L, int value)
    {
    	if (L == nullptr)
    	{
    		cout << "Error input L.";
    		return;
    	}
    
    	ListNode *pDelete = L;
    	if (L->m_value == value)
    	{
    		L = L->m_pNext;
    		delete pDelete;
    		return;
    	}
    	
    	ListNode *pFront = nullptr;
    	while (pDelete != nullptr && pDelete->m_value != value)
    	{
    		pFront = pDelete;
    		pDelete  = pDelete->m_pNext;
    	}
    
    	if (pDelete == nullptr)
    	{
    		cout << "Not found value in L.";
    		return;
    	}
    	if (pDelete->m_value == value)
    	{
    		pFront->m_pNext = pDelete->m_pNext;
    		delete pDelete;
    	}
    
    	return;
    }

    6. 翻转。

    ListNode * ReverseList(ListNode *L)
    {
    	if(L == nullptr || L->m_pNext == nullptr)
    		return nullptr;
    
    	ListNode * pCurrent = nullptr;
    
    	while (L != nullptr)
    	{
    		ListNode * tmp = L->m_pNext;
    		L->m_pNext = pCurrent;
    		pCurrent = L;
    		L = tmp;
    	}
    
    	/*DisplayList(L);
    	DisplayList(pCurrent);*/
    	return pCurrent;
    }

    7. 打印。

    void DisplayList(ListNode * L)
    {
    	ListNode * list = L;
    
    	while (list != nullptr)
    	{
    		cout << list->m_value << " -> ";
    		list = list->m_pNext;
    	}
    	cout << "null" << endl;
    }

    8. 链表长度。

    int ListLen(ListNode *L)
    {
    	int len = 0;
    
    	while (L != nullptr)
    	{
    		L = L->m_pNext;
    		++len;
    	}
    
    	return len;
    }

    9. 找到链表倒数第k个节点。

    ListNode *FindKthNode(ListNode *L, int k)
    {
    	if (ListLen(L) < k)
    		return nullptr;
    
    	ListNode *pFront = nullptr, *pBack = nullptr;
    
    	pFront = pBack = L;
    
    	int ii = 0;
    	while (ii++ < k-1)
    	{
    		pFront = pFront->m_pNext;
    	}
    
    	while (pFront->m_pNext != nullptr)
    	{
    		pFront = pFront->m_pNext;
    		pBack = pBack->m_pNext;
    	}
    
    	return pBack;
    }

    10. 合并两排序的链表。

    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
            if (pHead1 == nullptr)
    		  return pHead2;
    	  if (pHead2 == nullptr)
    		  return pHead1;
    
    	  ListNode *pNewHead = nullptr, *pNewTail = nullptr;
    	  ListNode *pNode1 = pHead1, *pNode2 = pHead2;
    
    	  while (pNode1 != nullptr && pNode2 != nullptr)
    	  {
    		ListNode *pTmp = nullptr;
    
    		if (pNode1->val < pNode2->val)
    		{
    			pTmp = pNode1;
    			pNode1 = pNode1->next;
    		}
    		else
    		{
    			pTmp = pNode2;
    			pNode2 = pNode2->next;
    		}
    
    		if (pNewHead == nullptr)
    		{
    			pNewHead = pNewTail = pTmp;
    		}
    		else
    		{
    			pNewTail->next = pTmp;
    			pNewTail = pNewTail->next;
    		}
    	  }
    
    	  if ( pNode1 == nullptr )  //和归并排序时的判断条件不一样。
    	  {
    		pNewTail->next = pNode2;
    	  }
    	  if ( pNode2 == nullptr )
    	  {
    		pNewTail->next  = pNode1;
    	  }
    
    	  return pNewHead;
    }

    11. 删除链表中重复的节点. 180420

    ListNode* deleteDuplication(ListNode* pHead)
    {
    	if(pHead == nullptr)
    		return nullptr;
    	if (pHead->next == nullptr)
    		return pHead;
    	if (pHead->val == pHead->next->val && pHead->next->next == nullptr)
    		return nullptr;
    
    	ListNode *pNewNode = new ListNode(-1);
    	pNewNode->next = pHead;
    	
    	ListNode *pLastNode = pNewNode;
    	ListNode *pCurNode = pHead;
    	ListNode *pNextNode = pHead->next;
    	int flag = 0;
    
    	while (pNextNode != nullptr)
    	{
    		while (pNextNode && pCurNode->val == pNextNode->val)
    		{
    			pNextNode = pNextNode->next;
    			pCurNode->next = pNextNode;
    			flag = 1;
    		}
    		
    		if ( flag )
    		{
    			pCurNode = pNextNode;
    			pLastNode->next = pCurNode;
    			flag = 0;
    
    			if (pNextNode)
    				pNextNode = pNextNode->next;
    		}
    
    		if (pNextNode && pCurNode->val != pNextNode->val)
    		{
    			pLastNode = pLastNode->next;
    			pCurNode = pCurNode->next;
    			pNextNode = pNextNode->next;
    		}
    
    	}
    
    	return pNewNode->next;
    }

    12. 找两个链表第一个公共节点。 180424

    struct ListNode {
    	int val;
    	struct ListNode *next;
    	ListNode(int x) :
    			val(x), next(NULL) {
    	}
    };
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2)
    {
          if (pHead1 == nullptr || pHead2 == nullptr)
          return nullptr;
            
          ListNode *pNode1 = pHead1, *pNode2 = pHead2;
    	int cnt1 = 0, cnt2 = 0;
    	while (pNode1)
    	{
    		cnt1++;
    		pNode1 = pNode1->next;
    	}
    	while (pNode2)
    	{
    		cnt2++;
    		pNode2 = pNode2->next;
    	}
    
    	pNode1 = (cnt1>cnt2) ? pHead1 : pHead2;
    	pNode2 = (cnt1>cnt2) ? pHead2 : pHead1;
    
    	for (int ii=0; ii!=abs(cnt1-cnt2); ++ii)
    		pNode1 = pNode1->next;
    
    	ListNode * pRet = nullptr;
    	while (pNode1 && pNode2)
    	{
    		if (pNode1 == pNode2)
    		{
    			pRet = pNode1;
    			break;
    		}
    		else
    		{
    			pNode1 = pNode1->next;
    			pNode2 = pNode2->next;
    		}
    	}
    
    	return pRet;
    }

    13. 复杂链表的复制。180424

    struct RandomListNode {
        int label;
        struct RandomListNode *next, *random;
        RandomListNode(int x) :
                label(x), next(NULL), random(NULL) {
        }
    };
    // ---------------牛客网大神通过的版本---------------
    RandomListNode* Clone(RandomListNode* pHead )
    {
    	if(!pHead)
    		return NULL;
    	RandomListNode *currNode = pHead;
    
    	while(currNode)
    	{
    		RandomListNode *node = new RandomListNode(currNode->label);
    		node->next = currNode->next;
    		currNode->next = node;
    		currNode = node->next;
    	}
    	currNode = pHead;
    	while(currNode)
    	{
    		RandomListNode *node = currNode->next;
    		if (currNode->random)
    			node->random = currNode->random->next;
    
    		currNode = node->next;
    	}
    	//拆分
    	RandomListNode *pCloneHead = pHead->next;
    	RandomListNode *tmp;
    	currNode = pHead;
    
    	while(currNode->next)
    	{
    		tmp = currNode->next;
    		currNode->next =tmp->next;
    		currNode = tmp;
    	}
    
    	return pCloneHead;
    }
    // ------------ 自己的版本,未通过,错误尚未找出-------------
    RandomListNode* Clone(RandomListNode* pHead)
    {
            if (pHead == nullptr)
    		  return nullptr;
    
    	  // ------------ 拷贝----------------
    	  RandomListNode *pNode = pHead;
    	  while (pNode)
    	  {
    		RandomListNode *pTmp = new RandomListNode(*pNode);
    		pTmp->next = pNode->next;
    		pNode->next = pTmp;
    
    		if (pNode->random)
    			pTmp->random = pNode->random->next;
    
    		pNode = pTmp->next;
    		pTmp = nullptr;
    	  }
    
    	  // ------------- 拆分-----------------
    	  pNode = pHead;
    	  RandomListNode *pNewHead = pHead->next;
    	  RandomListNode *pProcNode = pNewHead;
    
    	  if (pNode)   // 要让pNode走在pProcNode的前面。
    	  {
    		pNode->next = pProcNode->next;
    		pNode = pNode->next;
    	  }
    
    	  while (pProcNode && pNode)
    	  {
    		pProcNode->next = pNode->next;  // pNode后面不会是空,所以这样赋值可以确保pProcNode后面不会是空
    		pProcNode = pProcNode->next;
    		pNode->next = pProcNode->next;
    		pNode = pNode->next;
    	  }
    
    	  return pNewHead;
    }

    14. 寻找链表中环的入口节点。180425

    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
    	if(pHead == nullptr || pHead->next == nullptr)
    		return nullptr;
    
    	ListNode *pFast = pHead, *pLow = pHead;
    
    	// ----------- 获取环的大小------------
    	int cnt = 0;
    	pFast = pFast->next;
    	pFast = (pFast==nullptr) ? pFast : pFast->next;
    	pLow = pLow->next;
    	++cnt;
    
    	while (pFast != pLow && pFast)
    	{
    		pFast = pFast->next;
    		pFast = (pFast==nullptr) ? pFast : pFast->next;
    		pLow = pLow->next;
    		++cnt;
    	}
    
    	if (pFast == nullptr)
    		return nullptr;
    
    	// --------获取环的入口-----------
    	pFast = pLow = pHead;
    	ListNode *pRet = nullptr;
    	for (int ii=0; ii != cnt; ++ii)
    		pFast = pFast->next;
    	
    	while (pFast != pLow)
    	{
    		pFast = pFast->next;
    		pLow = pLow->next;
    	}
    	pRet = pFast;
    
    	return pRet;
    	
    }

    15. 主函数测试。

    int main(int argc, char **argv)
    {
    	ListNode * L = new ListNode;
    	InitList(L);
    
    	cout << "请输入链表节点,以-1结束:" << endl;
    	L = CreateListFromTail();
    	DisplayList(L);
    
    	int pos = 1, value = 10;
    	cout << "在指定位置 " << pos << " 前方插入节点:" << value << endl;
    	InsertListNodeFromFront(L, pos, value);
    	DisplayList(L);
    
    	pos = 2; value = 20;
    	cout << "在指定位置 " << pos << " 后方插入节点:" << value << endl;
    	InsertListNodeFromBack(L, pos, value);
    	DisplayList(L);
    
    	cout << "删除值为 " << value << " 的节点:"  << endl;
    	DeleteListNode(L, value);
    	DisplayList(L);
    
    	cout << "翻转链表:" << endl;
    	ListNode *R = ReverseList(L);
    	DisplayList(R);
    
    	getchar();getchar();
    	return 0;
    }

    测试结果:





    展开全文
  • C语言单链表的基本操作总结(增删改查)

    万次阅读 多人点赞 2019-01-27 22:56:17
    1.链表概述   链表是一种常见的数据结构。它与常见的数组是不同的,使用数组时先要指定数组包含元素的个数,即为数组的长度,但是如果向这个数组中加入的元素超过了数组的大小时,便不能将内容全部保存。   ...

    1.链表概述

      链表是一种常见的数据结构。它与常见的数组是不同的,使用数组时先要指定数组包含元素的个数,即为数组的长度,但是如果向这个数组中加入的元素超过了数组的大小时,便不能将内容全部保存。
      链表这种存储方式,其元素个数是不受限定的,当进行添加元素的时候存储的个数就会随之改变。
    图0

    图1
      链表一般有两种形式,有空头链表和无空头链表。
    图2
      在链表中有一个头指针变量,这个指针变量保存一个地址,通过这个地址来找到这个链表,头指针节点指向第一个节点,在链表中每个节点包含两个部分:数据部分和指针部分。虽然结构体不能含有与本身类型相同的结构,但是可以含有之相同类型结构的指针,这种定义是链表的基础,链表中每一项都包含在何处能找到下一项的信息。而最后一个节点的指针指向必须为空NULL,从链表的原理来看不用担心链表的长度会超出范围这种问题。

    2.链表的基本使用

    2.0 准备工作

      使用链表时,首先应包含一些基本的头文件,因为涉及到内存的操作和字符串的操作。

    #include "stdio.h"				
    #include "stdlib.h"				//提供malloc()和free()
    #include "string.h"				//提供strcpy()等
    

    malloc函数
    其函数原型如下:

    • void *malloc(unsigned int size);

    这个函数返回的是个void类型指针,所以在使用时应注意强制类型转换成需要的指针类型。
    free函数
    其函数原型如下:

    • void free(void *p);

    这个函数是用来释放指针p作指向的内存区。

    2.1 创建节点(结构体)

    struct Node
    {
    	int a;				//数据域
    	struct Node* next;	//指针域(指向节点的指针)
    };
    

    2.2 全局定义链表头尾指针 方便调用

    struct Node* head= NULL;
    struct Node* end = NULL;
    

    2.3 创建链表,实现在链表中增加一个数据(尾添加)————增

    void AddListTill(int a )
    {
    		//创建一个节点
    		struct Node* temp=(struct Node*)malloc(sizeof(struct Node));		//此处注意强制类型转换
    
    		//节点数据进行赋值
    		temp->a=a;
    		temp->next=NULL;		
    		
    		//连接分两种情况1.一个节点都没有2.已经有节点了,添加到尾巴上
    		if(NULL==head)
    		{	
    
    			head=temp;
    		//	end=temp;
    		}
    		else
    		{
    		end->next=temp;
    	//	end=temp;			//尾结点应该始终指向最后一个
    		}
    		end=temp;			//尾结点应该始终指向最后一个
    }
    

    AddListTill函数的功能是尾添加的方式在链表的尾部增加一个节点,其中输入的参数是这个节点的数据。首先创建一个节点并申请一个节点的内存,之后对传入节点的数据进行赋值,注意尾添加的新节点的指针应指向空;此时分两种情况,1是链表中一个节点都没有,那么这个节点既是头结点也是尾结点;2是已经有节点,那么新添加的节点将成为最后一个节点,而之前的节点因为成为了倒数第二个节点了所以它的指针应该指向新添加的节点,之后全局变量尾结点应该指向现在的节点(注意操作的先后顺序不能变)。

    2.4 遍历链表 —————查

    void ScanList()
    {
    	struct Node *temp =head;		//定义一个临时变量来指向头
    	while (temp !=NULL)
    	{
    		printf("%d\n",temp->a);
    		temp = temp->next;		//temp指向下一个的地址 即实现++操作
    	}
    
    }
    

    ScanList函数的功能是遍历这个链表,首先定义一个用于遍历的临时指针,用while循环实现遍历输出等操作。

    2.5 查询指定的节点 (遍历 一个个找)

    struct Node* FindNode(int a )
    {
    	struct Node *temp =head;
    	while(temp !=NULL)
    	{
    	if(a == temp->a)
    	{
    		return temp;
    	}
    	temp = temp->next;
    	}
    	//没找到
    		return NULL;
    } 
    

    FindNode函数的功能仍然是遍历链表,只不过会对每个节点中的数据进行一一判断,若找到则返回该节点,若没找到则返回NULL。

    2.6 链表清空——————全部删除

    void FreeList()
    {
    	//一个一个NULL
    	struct Node *temp =head;		//定义一个临时变量来指向头
    	while (temp !=NULL)
    	{
    	//	printf("%d\n",temp->a);
    		struct Node* pt =temp;
    		temp = temp->next;		//temp指向下一个的地址 即实现++操作
    		free(pt);					//释放当前
    	}
    	//头尾清空	不然下次的头就接着0x10
    	head =NULL;
    	end =NULL;
    }
    

    FreeList函数仍是采用遍历的方式一个一个的将节点内存释放,最后实现全部删除的效果,但是要注意在最后应该讲头尾节点至NULL否则下次的链表将会接着这次的头尾。

    2.7.在指定位置插入节点 ————在指定位置增

    void AddListRand(int index,int a)
    {	
    
        if (NULL==head)
    	{
    		printf("链表没有节点\n");
    		return;
    	}	
        struct Node* pt =FindNode(index);
    	if(NULL==pt)    //没有此节点
    	{
    		printf("没有指定节点\n");
    		return;
    	}
        //有此节点
        //创建临时节点,申请内存
    	struct Node* temp =(struct Node *)malloc(sizeof(struct Node));
    	//节点成员进行赋值
    	temp->a=a;
    	temp->next=NULL;
    	//连接到链表上 1.找到的节点在尾部 2.找到的节点在中间 
    	if (pt == end)
    	{
    	//尾巴的下一个指向新插入的节点
    	end->next=temp;
    	//新的尾巴
    	end=temp;
    	}else
    	{
    	// 先连后面 (先将要插入的节点指针指向原来找到节点的下一个)
    	temp->next=pt->next;
    	//后连前面
    	pt->next=temp;
    	}
    
    }
    

    首先要知道在指定节点插入的过程就像手拉手得人在一条线,这时来了一个新人,他要求站在甲之后,首先要找到甲的位置,如果链表为空或者没有甲则无法插入,如果链表不为空并且甲在这个链表中,则还要看甲是在链表中间还是甲就在最后的尾巴上,如果在尾巴上则新插入的即为尾巴如图1,若在甲乙之间则需要先连上后面乙再连上前面的甲,如图2。
    在这里插入图片描述
    在这里插入图片描述

    2.8尾删除————删

    
    void DeleteListTail()
    { 
    	if (NULL == end)
    	{
    		printf("链表为空,无需删除\n");
    		return;
    	}
    	//链表不为空 
    	//链表有一个节点
    	 if (head==end)
    	 {
    		 free(head);
    		 head=NULL;
    		 end=NULL; 
    	 }
    	 else
    	 {
    		//找到尾巴前一个节点
    		 struct Node* temp =head;
    		 while (temp->next!=end)
    		 {
    			 temp = temp->next;
    		 }
    		 //找到了,删尾巴
    		//释放尾巴
    		 free(end);
    		 //尾巴迁移
    		 end=temp;
    		 //尾巴指针为NULL
    		 end->next=NULL;
    	 }
    
    }
    

    尾删除的过程和前面一样首先应判断这个链表是不是为空或者只有一个节点,若只有一个节点则直接置NULL,若不为空,则先通过遍历找到倒数第二个节点,安徽将最后一个节点释放内存,再讲倒数第二个节点设置为end,然后将它的指针指向NULL。

    2.9 删除头——————删

    void DeleteListHead()
    {	//记住旧头
    	struct Node* temp=head;
    	//链表检测 
    	if (NULL==head)
    	{
    			printf("链表为空\n");
    			return;
    	}
    
    	head=head->next;//头的第二个节点变成新的头
    	free(temp);
    
    }
    

    先定义一个临时变量指向旧的头,将头的第二个记为新的头指针head,之后将旧的头释放

    2.10 删除指定节点

    void DeleteListRand(int a)
    {
    
    	//链表判断 是不是没有东西
    	if(NULL==head)
    	{
    	printf("链表没东西\n");
    	return;
    	}
        //链表有东西,找这个节点
    	struct Node* temp =FindNode(a);
    	if(NULL==temp)
    	{
    	printf("查无此点\n");
    	return;
    	}
    	//找到了,且只有一个节点
    	if(head==end)
    	{
    	free(head);
    	head=NULL;
    	end=NULL;
    	}
    	else if(head->next==end) //有两个节点
    	{
    	//看是删除头还是删除尾
    	if(end==temp)
    		{	DeleteListTail(); }
    	else if(temp==head)
    		{	DeleteListHead(); }	
    	}
    	else//多个节点
    	{
    		//看是删除头还是删除尾
    		if(end==temp)
    			DeleteListTail();
    		else if(temp==head)
    			DeleteListHead();	
    		else	//删除中间某个节点
    		{	//找要删除temp前一个,遍历
    			struct Node*pt =head;
    			while(pt->next!=temp)
    			{
    			pt=pt->next;
    			}
    			//找到了
    			//让前一个直接连接后一个 跳过指定的即可
    			 pt->next=temp->next;
    			 free(temp);
    		
    		}
    	}
    	
    
    }
    

    情况与前面增加类似不再赘述,具体见图
    在这里插入图片描述

    3. 测试主程序

    下面是测试用的主程序,主要实现了链表的增删查改等基本操作。

    void main ()
    {	
    	struct Node *pFind ;
    	//创建5个节点
    	for(i=0;i<6;i++)
    	AddListTill(i);
    	
    //	AddListRand(4,14);		//在指定位置4增加节点14
    //	DeleteListTail();		//删除一个尾结点
    	DeleteListRand(4);		//删除4节点
    	ScanList();				//便利输出链表
    	FreeList();				//删除链表
    /*	pFind = FindNode(5);	//查找5节点
    
    	if (pFind !=  NULL)
    	{
    		printf("找到%d\n",pFind->a);	//找到节点并且输出该节点数据
    	}
    	else
    	{
    		printf("No Find!\n");
    	}
    
    */
    
    }
    

    有关无空头的单链表的基本操作就总结到这里,当然还有双链表等更复杂的数据结构,以及遍历和查找的优化算法也有待进一步探索与学习。

    展开全文
  • 【C语言】链表及单链表基本操作

    万次阅读 多人点赞 2019-04-29 15:20:45
    1、什么是链表链表的分类? 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 数据结构中: 2、链表的分类 共有8种链表结构 ...
  • 单链表的基本操作

    万次阅读 多人点赞 2018-08-25 20:10:36
    链表:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称为存储单元为一个节点。 单链表的存储结构 typedef int DataType; typedef struct ListNode { DataType _data; //当前节点中...
  • 链表基本操作及应用 一、实验目的 1.掌握线性表的链式存储结构的表示和实现方法。 2.掌握单链表基本操作的算法实现。 3.了解单链表的应用。 二、实验环境 硬件环境要求: PC 机(单机) 使用的软件名称、版本号...
  • python实现单链表的基本操作

    千次阅读 多人点赞 2019-06-28 09:42:15
    单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以...
  • 链表基本操作

    2019-06-21 21:15:00
    实习目的:熟练掌握链表的建立及基本操作 问题描述: 1)实现链表的排序(升序) 2)实现两个有序链表的合并:A=A∪B,要求合并后仍然有序。 提交前请将所有的提示信息去掉,只保留最后的输出结果。例如运行时:从...
  • 【数据结构与算法笔记】单链表的基本操作

    万次阅读 多人点赞 2018-02-13 14:40:21
    顺序表在之前的博客有介绍过,不明白的朋友可查看:静态分配顺序表的基本操作及动态分配顺序表的基本操作。相对于顺序表来说,链表稍微难一些,本人花了两天的时间认真查看了一些资料,终于大致明白了一些东西。现在...
  • 入门:链表基本操作

    千次阅读 多人点赞 2015-11-05 00:15:02
    入门:链表基本操作标签:C语言 链表By 小威威1.写这篇博文的原因C语言有三大重要部分:流程控制、函数、指针。 对于指针,单单了解它的简单应用是不够的,最重要的还是学习链表。许多参考书对链表基本操作的...
  • C语言单链表基本操作总结

    千次阅读 多人点赞 2018-03-26 00:08:23
    C语言单链表基本操作 本文是参考他人实现的C语言单链表,对多篇博文整理的结果,仅作为学习笔记。文末有参考出处。1、单链表定义 链表是通过一组任意的存储单元来存储线性表中的数据元素,这些存储单元可以是连续...
  • c++单链表基本操作

    2015-10-27 18:49:44
    写个链表基本操作,还没完全测试。#include using namespace std; /*Node 节点*/ struct Node { public: Node(int d) { data = d; p = NULL; } int data; Node *p; }; /*单链表*/ class Link { priva
  • C++单链表基本操作

    千次阅读 多人点赞 2018-07-02 20:21:07
    #include &lt;iostream&gt; using namespace std; struct NODE { int data; NODE * next; }; class List ...则表示不带头结点的链表,只有头指针 List() { head = new NODE; head-&gt...
  • 链表(3)----循环单链表基本操作

    千次阅读 2014-12-09 19:35:51
    1、循环链表基本定义 typedef struct CListElement_t_{  void *data;  struct CListElement_t *next; } CListElement_t typedef struct CList_t_ {  int size;  int (*match)(const void *key1,
  • 数据结构之单链表基本操作(C++)

    千次阅读 2019-06-21 15:45:02
    #include<iostream> using namespace std; typedef int elemType; template <typename elemType> class linkList { private: struct Node //结点类型 { ... //结点数据...
  • 顺序表的创建、元素删除、遍历等操作: 有序的一组整数{1,2,3,4,6},设计顺序表并实现以下操作: A.初始化一个空的顺序表; B.从键盘依次输入上述数据添加到顺序表中; C.删除表中的第四个数据元素; D.显示B...
  • 单链表基本操作(不带头结点) 链表概念: 一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一节点 具体操作概述 尾插一个元素 尾删一个元素 头插一个元素 头删一个元素...
  • 单链表基本操作

    2020-11-05 11:56:30
    1单链表基本操作(5分) 请编写程序实现单链表插入、删除结点等基本算法。给定一个单链表和一系列插入、删除结点的操作序列,输出实施上述操作后的链表。单链表数据域值为整数。 输入格式: 输入第1行为1个正整数n,...
  • 链表可以看成一种在物理存储单元上的非连续、非顺序存储的数据结构,该数据结构中的结点(数据元素)逻辑顺序通过链表中指针链接次序实现。结点可以在程序运行是动态生成。每一个结点包括两部分:一个是存储数据元素...
  • 单链表基本操作 1.头插法建立单链表 2.尾插法建立单链表 3.查找结点 3.修改结点 4.插入结点 5.删除结点 本篇只有c语言代码,具体思路讲解请看这篇博客:数据结构-线性结构-单链表 1.头插法建立单链表 #include<...
  • 本文将单链表与双向链表基本操作在同一个程序中实现。其中单链表头文件中的函数与双向链表头文件中的函数可以分离出来单独使用。菜单程序的实现的程序较为复杂,变量多且作用范围不同,如果修改代码需要对代码非常...
  • 单链表基础概念 ...链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 特点 用一组 ...
  • 百科 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继...链表由头指针唯一确定,单链表...
  • 掌握单链表、循环链表、双向链表各种基本操作的实现方法。 实验内容: 实现单链表类型,为其设计演示系统展示其基本操作。 实现要求: 1)单链表带头结点,元素为整型,基本操作包括:初始化、创建、遍历...
  • C++实现链表基本操作

    万次阅读 多人点赞 2016-01-10 21:56:57
    前几天找实习的时候,一个面试官给我留了一个题,做一个链表demo,要求实现创建、插入、删除等操作链表是一种常见的数据结构,它是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中...
  • C语言 算法与数据结构 单链表 基本操作及实验案例 实验要求: 实现单链表的如下操作: 1.初始化、2.判空、3.清空、4.计数(长度)、5.按位置查找、6.按值查找、7.插入、8.删除、9.创建(头插法)、10.创建(尾插法)、11....
  • 实现带头结点单链表基本操作如逆序建立链表、插入、删除等操作。
  • 单链表基本操作–数据结构 链表是一种物理存储结构上非连续、非顺序的数据存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序来形成的。 单链表分为两种:带头节点和不带头结点。在这里,主要介绍不带头结点...
  • c++ 单链表基本操作

    万次阅读 2012-03-13 21:01:44
    #include #include #include #include .../*c++实现简单的单链表操作*/ using namespace std; typedef struct student { int data; struct student *next; }node; //建立单链表 node *creat() { n

空空如也

1 2 3 4 5 ... 20
收藏数 212,445
精华内容 84,978
关键字:

链表的基本操作