链表的补充

有环链表及其延伸的问题:


 
(1)判断是否有环。采用快慢指针的思路,一个指针一次移动一个结点,另一个指针移动两个结点。
    如果有环,则进入环后,快指针肯定可以追赶上慢指针,这样就会出现两者相等的情况,从而得出有环,但有一个前提,这个单链表必须有一个以上的结点在环之外才能 判断出入口地址。
(2)怎么来找环的入口问题。
    假定环外的长度为 l,相遇时环入口距离相遇处距离为 a,整个环为 x,则有下面的公式。
    l + a = (l + a + nx )/2 即 l = nx – a。
    这样,让快指针从这个位置开始继续移动,但步进为 1,慢指针从链表头开始移动,下次相遇的位置就是环的入口地址。
(3)找环的长度。
    现在已经知道环的入口位置,这样的话,采用快慢指针,再次相遇时快指针多移动的距离就是环的长度。

#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
    struct node *next;
    int data;
}Node;
/*
use insert before to get a link
then modify the tail->next value to local of the circle.
*/
Node *get_circle_link(int length, int local)
{
    Node *temp = NULL;
    Node *phead = NULL;
    Node *tail = NULL;
    int i=0; 
    // 建立一个长度为length的链表,链表的
    for(i=0; i<length; i++)
    {
        /* 建立节点 */
        temp = (Node *) malloc( sizeof (Node) );
        if(NULL == temp)
        {
            printf("malloc error");
            return NULL;
        }
        temp->next = NULL;
        temp->data = length-i; 
        /* 采用头插入的方式建立链表 */
        // 头节点的建立
        if(NULL == phead)   
            tail = temp;
        // 头插入
        temp->next = phead;
        phead = temp;
    }
    /* 建立链表中的环 */
    // 遍历节点,找到环的起点位置
    for(temp = phead,i=1;i< local; i++) 
    {
        temp = temp->next;
    }
    // 已经建立的链表的尾节点指向环节点的首节点
    tail->next = temp;
    return phead;
}
/* 链表的打印 */
void link_print(Node* temp)
{
    while(temp!=NULL)
    {
        printf("%p:%d\t",temp,temp->data);
        temp=temp->next;
        getchar();
    }
    printf("\n");
}
/* 判断有环链表 */
// 如何判断是否有环?
// 通过建立快慢指针,快指针的速度是慢指针的两倍
// 快慢指针可以相遇时,即为有环;
// 如何得到链表的环节点的首节点数据?
// l:直链长;a:环中相遇的位置;x:环的长度
// 公式 l+a = (l+a+nx)/2 --> l+a = nx
// n=1;l=x-a;
// 相遇后,快慢指针的步长均为1,慢指针回到头开始,快指针继续走;
int get_circle_local(Node *phead)
{
    Node *fast = phead;
    Node *slow = phead;


    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            for(slow = phead;slow!=fast;)
            {
                slow = slow->next;
                fast = fast->next;
            }
            return slow->data; 
/* 求出环的大小
			fast = fast->next->next;
			while(slow!=fast)
			{
				slow = slow->next;
                fast = fast->next->next;
				count++;
			}
			count++;
			return count;
*/
        }


    }
    return 0;
}




int main(int argc,char *argv[])
{
    Node *phead = NULL;
    int local=4;
    int number=10;


    phead=get_circle_link(number,local);
    //link_print(phead);
    int ret = 0;
    if( (ret = get_circle_local(phead)) >0)
        printf("has circle,local is %d\n",local);
    else
        printf("no circle\n");
        //printf("local=%d, ret=%d\n",local,ret);
    return 0;
}


问题的延伸:

1. 找出单链表的倒数第 4 个元素。找出单链表的中间元素。
     建立快慢指针,快指针步长为4,慢指针的步长为1;其实就是判断指向当前节点的顺数第四节点为NULL时,表明当前节点为目标节点; 
2.  判断两个单链表是否相交,若两个单链表相交,计算相交点。
     遍历其中的一个链表,将此链表的终节点指向当前链表的头节点,即将此链表构成了环,接下来判断另外一个链表是否有环,有环即两个单链表相交。

删除指定的节点

(1)对于非最后一个结点,将要删除结点的下一个结点数据拷贝到要删除结点,删除下一个结点空间
(2)因为最后一个结点没有办法通过上述方法解决,因此只能遍历。找到前向指针再删除
    时间复杂度:
    n-1 : O(1)
    1 :O(n)
    因 此,总的时间复杂度仍然为 O(1)。
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
    struct node *next;
    int data;
}Node;
Node *get_link(int length,int local,Node** plocal)
{
    Node *temp = NULL;
    Node *phead = NULL;
    Node *tail = NULL;
    int i=0;
    for(i=0; i<length; i++)
    {
        temp = (Node *) malloc( sizeof (Node) );
        temp->next = NULL;
        temp->data = length-i;
        if( phead == NULL)      //use insert before
            tail = temp;
        if(local == length - i)     //the del local address
            *plocal = temp;
        temp->next = phead;
        phead = temp;
    }
    return phead;
}


void link_print(Node* temp)
{
    while(temp!=NULL)
    {
        printf("%d\t",temp->data);
        temp=temp->next;
    }
    printf("\n");
}


Node* del_node(Node* phead, Node* local)
{
    if(phead == NULL || local == NULL)
    {
        printf("arg error\n");return NULL;
    }
    //(1) last node
    if(local->next == NULL)
    {
        if(phead == local) //only one node
        {
            free(local);
            return NULL;
        }
        Node *prev = phead;
        while(prev != NULL && prev->next != NULL)
        {
            if(prev->next == local)
            {
                prev->next = prev->next->next;
                free(local);
                return phead;
            }
            prev = prev->next;
        }
    }
    else
    {
        Node *temp = local->next;
        local->data = local->next->data;
        local->next = local->next->next;
        free(temp);
        return phead;
    }
}

如何实现单链表的反序输出?

1.将原链表进行反序,再进行遍历输出;
需要考虑时间复杂度的问题,翻转一次时间复杂度为O(n),空间复杂度为O(1);
缺点:需要修改原链表的属性,需要考虑并发的问题
2.使用栈的方式,栈的特点是先入后出;
链栈记录每个节点的地址          时间复杂度O(n)
将逐个弹出,输出相应的数据      空间复杂度O(n)
缺点:空间复杂度相对来说较大
3. 使用递归函数模拟栈的使用;
优点:代码简洁
缺点:递归函数的应用容易造成栈内存溢出的危险,效率并不高。
# include <stdio.h>  
# include <malloc.h>  
# define SIZE 5  //初始定义链表的长度  

//定义节点  
typedef struct Node  
{  
    int data;  
    struct Node * pNext;  
}NODE, * PNODE;  
//定义栈  
typedef struct Stack  
{  
    int * base;  //栈空间基址  
    int * top;  //指向栈顶有效元素的下一个位置  
}Stack;  


void traverse(PNODE pHead)  
{  
    printf("遍历链表:");  
    PNODE p = pHead->pNext;  
  
    while(p != NULL)  
    {  
        printf("%d ", p->data);  
        p = p->pNext;  
    }  
    printf("\n");  
}  
/* 反转链表,并遍历 */
// 链表逆序方法二
void reverseTrans_reverse(PNODE pHead)  
{  
    //反向链表的头结点  
    PNODE pRever = (PNODE)malloc(sizeof(NODE));  
    pRever->pNext = NULL;  
    PNODE p = pHead->pNext;  
    PNODE mid = NULL;  //两个临时变量  
    PNODE pTail;  
    while(p != NULL)  
    {  
        pTail = mid;  
        mid = p;  
        p = p->pNext;  
        mid->pNext = pTail;  
    }
    pRever->pNext = mid;  
    traverse(pRever);  
}   


// 链表逆序方法二
void reverseTrans_reverse2(PNODE pHead)
{
	PNODE prev = NULL;
	PNODE temp = pHead->pNext;
	PNODE next = NULL;
	
	while(temp!=NULL)
	{
		next = temp->pNext;
		temp->pNext = prev;
		prev = temp;
		temp = next;
	}
	// 遍历节点
	while(prev != NULL)
	{
		printf("%2d",prev->data);
		prev = prev->pNext;
	}
	printf("\n");
}


//根据栈,反向输出链表  
void reverseTrans_stack(PNODE pHead)      
{  
    int val;  
    Stack s = creat_stack();  //创建栈  
    PNODE p = pHead->pNext;   
	
    while(p != NULL)  
    {  
        push(s, p->data);     //节点从头到尾如入栈
		s.top++; 
        p = p->pNext;  
    }  
    while(s.top != s.base)    //元素再出栈  
    {  
        s.top--;  
        val = *(s.top);  
        printf("%d ", val);  
    }  
    printf("\n");  
}  
//创建栈  
Stack creat_stack()  
{  
    Stack s;  
    s.base = (int *)malloc(sizeof(int) * SIZE); //分配栈空间  
    s.top = s.base;  
    return s;  
}  
//元素入栈  
void push(Stack s, int val)  
{  
    if((s.top - s.base) > SIZE)  
    {  
        printf("栈满!\n");  
        return;  
    }  
    else  
    {  
        *(s.top) = val;  
        //s.top++; 
    }  
}  


/* 递归函数实现链表的逆序输出 */
void reverseTrans_Recursion(PNODE pHead)
{
	PNODE temp = pHead;


	if(NULL != temp->pNext)
	{
		reverseTrans_Recursion(temp->pNext);
		printf("%2d",temp->pNext->data);
	}
}
int main(void)  
{  
    int i, len, val=0;  
    //创建链表  
    PNODE pHead = (PNODE)malloc(sizeof(NODE));  //头结点  
    PNODE pTail = pHead;     					//尾节点  
    PNODE pNew;  								//新节点  
    printf("创建链表的长度为:len = %d\n", SIZE);  
    len = SIZE;  
    for(i = 0; i < len; i++)  
    {  
        val++;
        //创建新节点  
        pNew = (PNODE)malloc(sizeof(NODE));  
        pNew->data = val;  
        pNew->pNext = NULL;  
        //新节点添加在尾节点后面  
        pTail->pNext = pNew;  
        pTail = pNew;  
    }  
    //遍历链表  
    traverse(pHead);  
    //反向遍历链表, 从头到尾节点入栈,然后再出栈  
    printf("用栈,反向输出链表:");  
    reverseTrans_stack(pHead); 
    //反向遍历链表,反转链表  
    printf("反转链表:");  
    reverseTrans_reverse2(pHead);  
// 递归,反向输出链表
	printf("递归,反向输出链表:");
	reverseTrans_Recursion(pHead);
    return 0;  
} 

求出倒数第K个节点的数据

建立两个指针,一个指针先走K-1步,另一指针不动。接下来两个指针的步长均为1,那么当快的那个指针的下一个指针为NULL时,慢指针就是目标的节点。
需要考虑的问题包括如下:
1. 传进来的链表的头节点是否有效;
2. K是否有效,K为无符号整型数;
3. 链表的长度是否比K要长,容易造成段错误;
PNODE findKthList(PNODE pHead, unsigned int k)
{
	PNODE fast = pHead;
	PNODE slow = pHead;
	
	if(NULL == pHead || 0 == k)
	{
		return NULL;
	}
	
	for(unsigned int i=0; i<k-1; i++)
	{
		fast = fast->pNext;
		if(NULL == fast)
		{
			return NULL;
		}
	}
	
	while(NULL != fast)
	{
		fast = fast->pNext;
		slow = slow->pNext;
	}
	
	return slow;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值