• 《cracking the coding intreview》——链表


    前言

    最近准备暑假回家回家修整一下,所以时间大部分用来完成项目上的工作,同时为了9月份的校招,晚上的时间我还在学习<cracking the coding intreview>,第二章链表有几个不错的题目,记录一下

     

    单链表

    题目: Implement an algorithm to find the nth to last element of a singly linked list.

    译文: 实现一个算法从一个单链表中返回倒数第n个元素

    思路

    7个节点的示例链表图如下:



    例如我们找倒数第3个节点5,有两种思路

    (1)遍历一遍链表,获取链表的总数len,这样倒数第n个节点也就是正数第(len - n + 1)个节点,时间复杂度为O(2n)

    (2)很tricky做法,使用两个指针p,q,p指向head节点,q先前进n个节点,然后两个指针依次指向下一个,到q为NULL时,p为倒数第n个节点,时间复杂度为O(n)

    代码(c语言)

    采用第二种做法,因此时间复杂度更低,而且感觉更牛逼一些

    /**
     * Implement an algorithm to find the nth to last element of a singly linked list
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct link {
    	int value;
    	struct link *next;
    } link;
    
    /**
     * 创建单链表
     *
     * T = O(n)
     *
     */
    void createLinklist(link **head, int data)
    {
    	link *pre, *cur, *new;
    	cur = *head;
    	pre = NULL;
    
    	while (cur != NULL) {
    		pre = cur;
    		cur = cur->next;
    	}
    
    	new = (link *)malloc(sizeof(link));
    	new->value = data;
    
    	if (pre == NULL)
    		*head = new;
    	else
    		pre->next = new;
    }
    
    /**
     * 打印单链表
     *
     * T = O(n)
     */
    void printLinklist(link *head)
    {
    	while (head->next != NULL) {
    		printf("%d ", head->value);
    		head = head->next;
    	}
    	printf("%d
    ", head->value);
    }
    
    /**
     * 寻找单链表倒数第m个节点
     *
     * T = O(n)
     *
     */
    void findMthToLast(link *head, int m)
    {
    	if (head == NULL) return;
    	link *s1, *s2;
    	int i;
    	// s1指向表头,s2指向从s1开始后m个元素的位置,然后s1与s2同时后移,到s2为NULL时停止,s1为mth to last
    	s1 = s2 = head;
    
    	for (i = 0; i < m; i ++) {
    		s2 = s2->next;
    	}
    
    	while (s2 != NULL) {
    		s1 = s1->next;
    		s2 = s2->next;
    	}
    
    	printf("%d
    ", s1->value);
    }
    
    int main(void)
    {
    	int i, n, m, data;
    	link *head;
    
    	while (scanf("%d", &n) != EOF) {
    		for (i = 0, head = NULL; i < n; i ++) {
    			scanf("%d", &data);
    			createLinklist(&head, data);			
    		}	
    
    		// 接收mth to last
    		scanf("%d", &m);
    		
    		if (m > n) {
    			printf("输入数据有误!
    ");
    		} else {
    			printLinklist(head);
    			findMthToLast(head, m);
    		}
    	}
    
    	return 0;
    }


    循环链表

    题目:给定一个循环链表,实现一个算法返回这个环开始的结点

    例子:

    输入 : A->B->C->D->E->C[节点C在之前已经出现过]
    输出:节点C

    思路

    先来个图示:




    参考上面的做法,我们也设置两个指针,fast和slow,first一次走两个节点,slow一次走一个节点,从head出发,最后必然在循环内某个节点相遇,我们模拟一下:

    1. s->B  f->D
    2. s->C  f->C

    (ps:不是每次都在起始点相遇哈,这里是巧合)

    保持f不动,s继续移动,记录移动的次数,当再次到达f是,当前次数即为循环的长度len

    然后定义两个节点p,q,p指向表头,q向前移动len步,然后一次走到p和q相遇,此节点极为链表的循环起始节点

    代码(c语言)

    /**
     * Given a circular linked list, implement an algorithm which returns
     * node at the begining of the loop
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct link {
    	int value;
    	struct link *next;
    } link;
    
    /**
     * 创建单链表
     *
     * T = O(n)
     *
     */
    void createLinklist(link **head, int data)
    {	
    	link *cur, *pre, *new;
    
    	cur = *head;
    	pre = NULL;
    
    	while (cur != NULL) {
    		pre = cur;
    		cur = cur->next;
    	}
    
    	new = (link *)malloc(sizeof(link));
    	new->value = data;
    	new->next = cur;
    
    	if (pre == NULL) {
    		*head = new;
    	} else {
    		pre->next = new;
    	}
    }
    
    /**
     * 从m个节点开始构建循环链表
     *
     * T = O(n)
     *
     */
    void initLoopList(link *head, int m)
    {
    	link *cur, *pre, *target;
    	cur = head;
    
    	while (-- m && cur != NULL) {
    		cur = cur->next;
    	}
    	target = cur;
    
    	while (cur != NULL) {
    		pre = cur;
    		cur = cur->next;
    	}
    	pre->next = target;
    }
    
    /**
     * 寻找循环开始点的value
     *
     * T = O(n)
     *
     */
    void loopStart(link *head)
    {
    	link *fast, *slow, *p, *q;
    	int i, len;
    
    	for (slow = head, fast = slow->next->next; fast != slow;) {
    		slow = slow->next;
    		fast = fast->next->next;
    	}
    
    	for (len = 1, slow = slow->next; slow != fast; slow = slow->next) {
    		len += 1;
    	}
    
    	p = q = head;
    	for (i = 0; i < len; i ++) {
    		q = q->next;
    	}
    
    	while (p != q) {
    		p = p->next;
    		q = q->next;
    	}	
    	printf("%d
    ", q->value);
    }
    
    int main(void)
    {
    	link *head;
    	int i, n, m, data;
    
    	while (scanf("%d", &n) != EOF) {
    		// 创建单链表
    		for (i = 0, head = NULL; i < n; i ++) {
    			scanf("%d", &data);
    			createLinklist(&head, data);
    		}
    
    		// 第m个点开始循环
    		scanf("%d", &m);
    		if (m > n) continue;
    		initLoopList(head, m);
    		
    		// 查找循环起始节点(只提供头节点)
    		loopStart(head);
    	}
    
    	return 0;
    }
    







  • 相关阅读:
    PHP5中__call、__get、__set、__clone、__sleep、__wakeup的用法
    MyISAM InnoDB 区别
    Mozilla推荐的CSS书写顺序
    转:Javascript异步编程的4种方法
    从一个实例,看new FunctionName()的内部机制
    矩阵转置 O(1)空间
    不要将 Array、Object 等类型指定给 prototype
    javascript线程解释(setTimeout,setInterval你不知道的事)
    JS 中没有按地址(引用)传递,只有按值传递
    输入一个无符号整数,用最少的步骤将该数变为1
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3225831.html
Copyright © 2020-2023  润新知