链表的简单实现

      链表属于线性表的范畴。线性表有顺序的存储结构实现,代码如下:

typedef int ElemType;
typedef struct{
  Elem elem[maxsize];
  int length;
}SqList;

同时线性表还有链式存储结构,包括单链表,双链表,循环链表,静态链表。

      单链表:只能向前遍历,最后一个节点为NULL,判断结束用while(p)。编程的时候只要处理next指针就行了。

      双链表:可以两个方向遍历,双链表的尾节点指针为NULL。在编程的时候注意要处理next和prev指针,其他和单链表一样。

      循环链表:只能向前遍历,但是最后一个指向头结点。这样判断结束的时候用while(p!=head)。编程的时候注意最后一个节点的处理。

      静态链表:借用一维数组来描述线性链表。数组中的一个分量表示一个节点,同时使用游标代替指针指示下一个节点在数组中的位置,第0个节点可以不存数,表示Head节点。这种存储结构需要首先分配很大的空间,但是在进行插入和删除的时候不需要移动元素,仅需要修改游标就行了。

typedef char ElemType[10]
typedef struct{
     ElemType data;
     int next;
}StaticList[MaxSize];
 初始化的时候所有元素的next = -1,最后一个节点next = 0;删除元素的时候next = -1 

      本文只简单的写了链表的操作。


链表的实现

     好久没用C语言写代码了,手生了,得找点感觉,不然面试就悲剧了。本文尽量采用C语言的风格,可是写出的代码还是夹杂着C++风格,关键我用的visual studio。简单的总结一下,后面复习的时候用到。链表实现的时候主要注意一下几点:

     (1)链表编代码的时候不要忘了最后一个节点加上“NULL”,C++里面有构造函数,构造出来next就是NULL,所以出现这个问题比较少。

     (2)链表的插入和删除操作,需要扫一遍链表,先判定是否存在该节点。

     (3)链表的创建有头插法和尾插法两种。头插法创造出来的链表是反的,尾插法就是顺序的。

     (4)不要忘了释放链表。(本文没有编写释放)

     (5)在子函数里面分配节点有如下两种方法:

//方法1
int *test()
{
    int *t = (int*)malloc(sizeof(int));
    return t; 
}
//方法2
void test(int **t)
{
   *t = (int*)malloc(sizeof(int));
}
test(&pHead);//调用


链表实现代码:

      这边只实现了几个简单操作,其他应该也不难。我这边实现的代码都有一个额外的头结点。

#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;

typedef int ElemType;

typedef struct ListNode{
    ElemType data;
    struct ListNode* next;
}LinkList;

void InitNode(LinkList **head)
{
    *head = (LinkList*)malloc(sizeof(LinkList));
    (*head)->next = NULL;
}

void CreateList(LinkList* pHead,ElemType *arr,int len)
{
    LinkList *pCurrent = pHead;
    for (int i=0;i<len;++i)
    {
	    LinkList *newNode = (LinkList*)malloc(sizeof(LinkList));
	    if(!newNode){
   	         --i;
	        continue;
	    }
	    newNode->data = arr[i];
	    pCurrent->next = newNode;
	    pCurrent = newNode;
    }
    pCurrent->next = NULL;//别忘掉
}


void DisplayList(LinkList* pHead)
{
    if(!pHead)return;
    for(pHead=pHead->next;pHead;pHead=pHead->next)
	printf("%d ",pHead->data);
}


链表的常用操作

(1)链表的排序

方法1:选择排序

       这里我简单借助了选择排序,我并没有直接对节点进行重组,而只是去交换节点的值,代码测试没有问题。

void SortList(LinkList* pHead)
{
   LinkList *minPtr;
   for(LinkList* ptrA=pHead->next;ptrA&&ptrA->next;ptrA =ptrA->next){
       minPtr = ptrA;
       for(LinkList *ptrB =ptrA->next;ptrB;ptrB = ptrB->next){
           if(ptrB->data<minPtr->data)
               minPtr = ptrB;
       }
       if(minPtr!=ptrA)
       {               
           ElemType temp = ptrA->data;
           ptrA->data = minPtr->data;
           minPtr->data =temp; 
        } 
   }
}

方法2:(插入排序)


void InsertSortList(LinkList* pHead)
{
    if (!pHead->next)return;
    LinkList *p,*q = pHead->next->next;
    pHead->next->next = NULL;//将第一节点断开
    while(q){
	    LinkList *tmp = q->next;
	//p->next,p节点不为NULL
	    for(p = pHead;p->next&&p->next->data<q->data;p=p->next);
        q->next = p->next;
        p->next = q;
        q = tmp;
    }

}


(2)链表的反序操作

       画个图简单的说明下。


void ReverseList(LinkList *pHead)
{
    if(NULL == pHead->next)return;
    LinkList *ptr = pHead->next->next;
    LinkList *pCurrent = pHead->next;
    pCurrent->next = NULL;//一定要
    while(ptr){
        LinkList *tmp = ptr->next;
        ptr->next = pCurrent;
        pHead->next = ptr;
        pCurrent = ptr;
        ptr = tmp;
   }
}

贴一个递归的代码,感觉写的不是很好,有没有更好的:

LinkList* ReverseListRecursively(LinkList *pHead,LinkList **pReverseHead)
{
     if(pHead==NULL) return NULL;
     if(pHead->next == NULL)
     {
	  *pReverseHead = pHead;
	  return pHead;
     }
     LinkList *pRet = ReverseListRecursively(pHead->next,pReverseHead);
     pRet->next = pHead;
     pHead->next = NULL;
}

呵呵:这哥们代码不错:点击打开链接

  1. LinkNode *reverse_link(LinkNode *head)  
  2. {  
  3.     if(head == NULL)  
  4.         return NULL;  
  5.     LinkNode *prev , *curr , *reverse_head , *temp;  
  6.     prev = NULL , curr = head;  
  7.     while(curr->next)  
  8.     {  
  9.         temp = curr->next;  
  10.         curr->next = prev;  
  11.         prev = curr;  
  12.         curr = temp;  
  13.     }  
  14.     curr->next = prev;  
  15.     reverse_head = curr;  
  16.     return reverse_head;  
  17. }  
  18.   
  19. LinkNode *reverse_link_recursive(LinkNode *head)  
  20. {  
  21.     if(head == NULL)  
  22.         return NULL;  
  23.     LinkNode *curr , *reverse_head , *temp;  
  24.     if(head->next == NULL)    // 链表中只有一个节点,逆转后的头指针不变  
  25.         return head;  
  26.     else  
  27.     {  
  28.         curr = head;  
  29.         temp = head->next;    // temp为(a2,...an)的头指针  
  30.         reverse_head = reverse_link_recursive(temp);   // 逆转链表(a2,...an),并返回逆转后的头指针  
  31.         temp->next = curr;    // 将a1链接在a2之后  
  32.         curr->next = NULL;  
  33.     }  
  34.     return reverse_head;      // (a2,...an)逆转链表的头指针即为(a1,a2,...an)逆转链表的头指针  
  35. }  

(3)两个有序链表的合并操作

void MergeList(LinkList* pHead1,LinkList*pHead2,LinkList* pHead)
{
   LinkList *tmp = pHead1;
   pHead1 = pHead1->next;
   free(tmp);
   tmp = pHead2;
   pHead2 = pHead2->next;
   free(tmp);
   while(pHead1&&pHead2){
        if(pHead1->data<pHead2->data){
            pHead->next = pHead1;
            pHead = pHead->next;
            pHead1 = pHead1->next;
        }
        elseif(pHead1->data>pHead2->data){
            pHead->next = pHead2;
            pHead = pHead->next;
            pHead2 = pHead2->next;
        }
        else{
            pHead->next = pHead1;
            pHead1 = pHead1->next;
            tmp = pHead2;
            pHead2 = pHead2->next;
            free(tmp);
            pHead = pHead->next;
        }
   }
   if(pHead1)pHead->next = pHead1;
   if(pHead2)pHead->next = pHead2;
}

(4)三个有序链表A,B,C。现在设计一种算法使A中只保留A,B,C中的相同的节点,其他释放掉

void CommmonNode(LinkList* hA,LinkList* hB,LinkList *hC)
{
     LinkList *pA = hA->next,*pB = hB->next,*pC = hC->next;
     hA->next = NULL;
     LinkList *pCur = hA;
     while(pA){
        while(pB&&pB->data<pA->data)pB = pB->next;
	    while(pC&&pC->data<pA->data)pC = pC->next;
	    if(pC->data==pA->data&&pB->data==pA->data){
		   pCur->next = pA;
		   pCur = pA;
		   pA = pA->next;
	    }
	    else{
		   LinkList *tmp = pA;
		   pA = pA->next;
		   free(tmp);
	    }
	   pCur->next = NULL;
	}
}

(5)求出链表中倒数第K个元素

       解法:很清晰就是用两个指针,第一个指针先跑到第K个元素,然后两个指针一起跑。但是面试写代码要注意考虑表的长度不够或者空表的现象。

ElemType TheLastKthNode(LinkList *pHead,int k)
{
     if(!pHead&&!pHead->next){
	     cerr<<"Empty list"<<endl;
	     return -1;
     }
     LinkList *pFirst = pHead->next;
     LinkList *pSecond = pHead->next;
     int i;
     for(i=0;pFirst&&i<k;i++,pFirst=pFirst->next);
     if(i!=k){
	     cerr<<"No enough nodes"<<endl;
	     return -1;
     } 
     for(;pFirst;pFirst=pFirst->next,pSecond=pSecond->next);
     return pSecond->data;
}

(6)求两个链表的交点问题:用长度的方法或者判断链表是否带圈。(不给出代码)

(7)链表没有头结点,但是要删除一个节点。(编程之美狸猫换太子)

(8)测试代码

int main()
{
    LinkList *pHead1,*pHead2,*pHead;
    InitNode(&pHead1);
    InitNode(&pHead2);
    InitNode(&pHead);
    ElemTypearr1[]={3,2,5,4};
    ElemType arr2[]={3,5,2,8};
    CreateList(pHead1,arr1,4);
    CreateList(pHead2,arr2,4);
    SortList(pHead1);
    SortList(pHead2);
    DisplayList(pHead1);
    cout<<endl;//c++
    DisplayList(pHead2);
    cout<<endl;//c++
    MergeList(pHead1,pHead2,pHead);
    DisplayList(pHead);
    system("pause");
    return 0;
}

    希望后面还有链表算法的时候,还能增加在这里。

 

后记

     新手,勿笑。

 


  

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值