数据结构学习笔记 - 链表

一下是链表的常见的题型

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解法一 头插法:就是用三个指针不断对前三个节点进行链表的头插
struct ListNode* reverseList(struct ListNode* head){

if(head==NULL) {
return head;
}
struct  ListNode  *p , *next ,* cur;
p = NULL;
cur = head;
next = NULL;
while(cur){
next = cur->next;
cur->next = p; 
p = cur;
cur = next;
}
return p;
}

解法二 递归
, Node *newhead = reverseList(head->next); // 获取到反转后的新的头结点,不管具体内容,把整个从head->next看做一个整体
//再将当前节点设置为后面节点的后续节点

head->next->next = head;//将当前head指向的那个节点的下一个节点的下一个节点指向head,就把head和head.next的方向换过来了。

head->next = NULL;//然后把它自己本身的head.next设置为null,就好了。
数据结构学习笔记 - 链表_第1张图片

 	Node * reverseList(List head) {
   
    //如果链表为空或者链表中只有一个元素
    
    if(head == NULL || head->next == NULL) {
        
        return head;
    }
  
    else
   
   
    {
        //先反转后面的链表,走到链表的末端结点
   
       Node *newhead = reverseList(head->next);
      
        //再将当前节点设置为后面节点的后续节点
       
        head->next->next = head;
        
        head->next = NULL;
        
 		 return newhead;
 		 
	}

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

解法快慢指针 设两指针fast slow指向链表头部head,迭代:
fast每轮走两步,slow每轮走一步,这样两指针每轮后距离+1;
若链表中存在环,fast和slow一定会在将来相遇(距离连续+1,没有跳跃);
若fast走到了链表尾部,则说明链表无环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    //     int data = head->val;
    //     struct ListNode **d;
    // d = &head->next;
    // while(head)
    // {
    //      struct ListNode*p;
    //     p = head;
    //     if(&p==d&&p->val==data)
    //     {
    //         return true;
    //     }
    //     head=head->next;
    // }
    // return false;
     
{  
    if (NULL == head)  
        return false;  
  
 struct ListNode *fast = head;  
    struct ListNode *slow = head;  
  
    /*判断是否存在环*/  
    while (fast->next != NULL)  //两种情况会跳出循环    
    {  
        fast = fast->next->next;  
        slow = slow->next;  
  
        if (NULL == fast)  
            return false;  
        if (fast == slow)  
            return true;
    }  
  
    if (NULL == fast->next)  //判断是哪种情况导致跳出循环    
        return false;  
  
    /*查找环起点*/  
//     fast = pHead;  
//     while (fast != slow)  
//     {  
//         fast = fast->pnext;  
//         slow = slow->pnext;  
//     }  
  
//     return true;  
return false;
}
}

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
解法满足一下条件的时候结束即可
终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
返回值:每一层调用都返回排序好的链表头
本级递归内容:如果 l1 的 val 值更小,则将 l1.next 与排序好的链表头相接

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
        if(l1==NULL)
            return l2;
        if(l2==NULL)
            return l1;
        struct ListNode *head , *p1 , *p2,*move;
        if(l1->valval) {
            head =l1;
            p1 = l1->next;
            p2 = l2;
        }
        else {
            head = l2;
            p1 = l1;
            p2 = l2->next;
        }
        move = head;
        while(p1&&p2)
        {
            if(p1->val <= p2->val)
            {
                move->next = p1;
                move = p1;
                p1 = p1->next;
                }
            else
            {
                move->next = p2;
                move = p2;
                p2 = p2->next;
            }
        }
        if(p1)
        {
            move->next = p1;
    }
    else
    {
        move->next = p2;
    }
    return head;
}

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

解法一
这个题之前做过一次第一次没有考虑那么多就是先便利一遍找到一共的节点个数然后在删除,同时考虑一些特殊情况即可代码如下

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
        struct ListNode*x;
            int cnt,cnt1;
    x=head;
    cnt=0;
    while(x){
        x=x->next;
        cnt++;
    }
    //printf("%d",cnt);
    cnt1=cnt;
    if(cnt==1){
        return NULL;
}
    if(cnt==2)
    {
        if(n==1){
            head->next=NULL;
            return head;
        }
        else
        return head->next;
}

x=head;

if(cnt1==n)

{
    
    return head->next;
}
while(cnt!=n+1){
    n++;
    x=x->next;
}

x->next=x->next->next;
    return head;

}
解法二 遍历一次 就还是用快慢指针
整体思路是让前面的指针先移动n步,之后前后指针共同移动直到前面的指针到尾部为止

	struct ListNode* removeNthFromEnd(struct ListNode* head, int n){   
	      struct ListNode *start, *end , *pre,*q;
	   // pre->next = head;
	

        pre = (struct ListNode*)malloc(sizeof(struct ListNode));
        pre->next = head;

     start = pre, end = pre;
        while(n != 0) {
            start = start->next;
            n--;
        }
  
        while(start->next!=NULL) {
            start = start->next;
            end = end->next;
        }
    
    if(head->next==NULL)
        return NULL;
    
  
 if(end->next==head)
 {
     return head->next;
 }
        end->next = end->next->next;
        return head;
    }

链表的中间节点

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

解法 同样还是使用快慢指针

struct ListNode* middleNode(struct ListNode* head){
 struct ListNode* slow = head;
 struct ListNode* fast = head;
        while (fast&&fast->next) {
            slow = slow->next;
            fast = fast->next->next;        
        }
        return slow;
}
##  约瑟夫环
编号为1,2,…,n的n个人按顺时针方向围坐在一张圆桌周围,每人持有一个密码(正整数)。一

开始任选一个正整数m作为报数上限值,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数,报m的那

个人出列,将他的密码作为新的m值,从他顺时针方向的下一个人开始重新从1报数,数到m的那个人又出列;如

此下去,直至圆桌周围的人全部出列为止。要求按出列顺序输出n个人的编号。

	#include 
#include 
#define MAX 100
typedef struct NodeType {
	int id;
	int password;
	struct NodeType *next;
} NodeType;
void CreaList(NodeType **, int); //创建单项循环链表
NodeType *GetNode(int, int); //得到一个节点
void PrintList(NodeType *); //打印循环链表
int IsEmptyList (NodeType *); //测试链表是否为空
void JosephusOperate (NodeType **, int); //运行约瑟夫环问题
int main(void) {
	int n = 0;
	int m = 0;
	NodeType *pHead = NULL;
	do {
		//人数n超过最大人数,重新输入人数n直至满足条件为止
		if (n > MAX) {
			printf("人数太多,请重新输入!\n");
		}
//		printf("请输入人数n(最多%d个):", MAX);
		scanf("%d", &n);
	} while (n > MAX);
//		printf("请输入初始密码m:");
		scanf("%d", &m);
		CreaList(&pHead, n); //创建单项循环链表
//		printf("\n------------打印循环链表----------------\n");
//		PrintList(pHead); //打印循环链表
//		printf("\n------------打印出队情况----------------\n");
		JosephusOperate(&pHead, m); //运行约瑟夫环问题
		return 1; 
}
//创建有n个节点的循环链表ppHead
void CreaList(NodeType **ppHead, int n) {
	int i = 0;
	int iPassword = 0;
	NodeType *pNew = NULL;
	NodeType *pCur = NULL;
	for (int i = 1; i <= n; i++) {
//		printf("输入第%d个人的密码:", i);
		scanf("%d", &iPassword);
		pNew = GetNode(i, iPassword);
		if (*ppHead == NULL) {
			*ppHead = pCur = pNew;
			pCur -> next = *ppHead;
		} else {
			pNew -> next = pCur -> next;
			pCur -> next = pNew;
			pCur = pNew;
		}
	}
//	printf("完成单项循环链表的创建!\n");
}
NodeType *GetNode(int iId, int iPassword) {
	NodeType *pNew = NULL;
	pNew = (NodeType *) malloc (sizeof(NodeType));
	if (!pNew) {
		printf("Error, the memory is not enough!\n");
		exit(-1); 
	} 
	pNew -> id = iId;
	pNew -> password = iPassword;
	pNew -> next = NULL; //pNew的next指向空,置空表尾 
	return pNew;
}
//依次输出至n个人,且输出密码,完成原始链表的打印
void PrintList(NodeType *pHead) {
	NodeType *pCur = pHead;
	//调用EmptyList()函数来判断if语句是否执行,若pHead为空则执行
	if (!IsEmptyList(pHead)) {
		printf("--ID----PASSWORD--\n");
		do {
			printf("%3d %7d\n", pCur -> id, pCur -> password);
			pCur = pCur -> next;
		} while (pCur != pHead);
	} 
} 
int IsEmptyList(NodeType *pHead) {
	if(!pHead) {
		//若 pHead 为空,提示“空”, 返回值
		printf("The list is empty!\n");
		return 1; 
	}
	return 0;
}
void JosephusOperate(NodeType **ppHead, int iPassword) {
	int iCounter = 0;
	int iFlag = 1;
	NodeType *pPrv = NULL;
	NodeType *pCur = NULL;
	NodeType *pDel = NULL;
	pPrv = pCur = *ppHead;
	//将pPrv 初始为指向尾结点,为删除做好准备
	while(pPrv -> next != *ppHead)
		pPrv = pPrv -> next;
	while(iFlag) {
		for(iCounter = 1; iCounter < iPassword; iCounter++) {
			pPrv = pCur;
			pCur = pCur -> next; 
		}
		if(pPrv == pCur)
			iFlag = 0;
		pDel = pCur; //删除pCur指向的节点,即有人出列
		//使得pPrv指向节点与下下一个节点相连,让pPcur从链表中脱节
		pPrv -> next = pCur -> next;
		//让指针pCur改为指向后继节点,后移一个节点 
		pCur = pCur -> next; 
		iPassword = pDel -> password; //记录出列的人手中的密码
//		printf("第%d个人出列(密码:%d)\n", pDel -> id, pDel -> password);
		printf("%d ", pDel -> id);
		free(pDel); //释放删除pDel指向的节点 
	} 
	*ppHead = NULL;
	getchar();
}

你可能感兴趣的:(数据结构学习笔记 - 链表)