4.链表入门

单双链表及其反转-堆栈诠释

        值引用:函数调用得到原值的拷贝,函数实际使用的变量与原变量解耦。

        引用传递:函数调用拷贝原指针得到一个新指针,该指针和原指针指向同一个内存区域。

反转单链表

        题干:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

        使用双指针法,使用head进行遍历,pre指向head前一个节点,next指向head后一个节点。在每轮,首先得到head的下一个节点(为了在断掉这个节点的next后能够找到下一个节点),让head.next指向pre,然后pre指向当前节点,head指向原来的下一个节点next。

func ReverseList(head *Node) *Node {
	var pre *Node
	var next *Node
	for head != nil {
		next = head.next
		head.next = pre
		pre = head
		head = next
	}
	return pre
}

反转双链表

        题干:给你双链表的头节点 head ,请你反转链表,并返回反转后的链表。

        和反转单链表一样,唯一不同增加一个把当前节点的last指向next的操作。

func ReverseDoubleList(head *Node) *Node {
	var pre *Node
	var next *Node
	for head != nil {
		next = head.next
		head.next = pre
		head.last = next
		pre = head
		head = next
	}
	return pre
}

链表入门题目-合并两个有序链表

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

        首先确定新链表的首节点head,pre指向head。然后list1和list2不断往后遍历。随后循环比较大小,确定pre下一个要链接的节点。最后如果有一个为空,则直接链接另一个剩余的所有节点。引用传递直接使用list1和list2也没问题。

func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
	if list1 == nil {
		return list2
	} else if list2 == nil {
		return list1
	}
	var head, pre *ListNode
	if list1.Val > list2.Val {
		head = list2
		list2 = head.Next
	} else {
		head = list1
		list1 = head.Next
	}
	pre = head
	for list1 != nil && list2 != nil {
		if list1.Val > list2.Val {
			pre.Next = list2
			list2 = list2.Next
		} else {
			pre.Next = list1
			list1 = list1.Next
		}
		pre = pre.Next
	}
	if list1 == nil {
		pre.Next = list2
	} else if list2 == nil {
		pre.Next = list1
	}
	return head
}

链表入门题目-两个链表相加

        给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

        使用temp存储当前数,temp%10是当前要存储的数,temp/10是判断是否有进位。这里在每轮循环让cur创建节点并对其赋值是为了保证不会在最后创建无值节点。保证除了头节点之后的所有节点的值是由temp决定的。否则如果先对当前节点赋值后创建下一个节点,则在计算的最后一轮给当前节点赋值后会再创建一个值,从而造成错误结果。

func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
	temp := 0
	head := &ListNode{}
	var cur = head
	for l1 != nil || l2 != nil {
		if l1 != nil {
			temp += l1.Val
			l1 = l1.Next
		}
		if l2 != nil {
			temp += l2.Val
			l2 = l2.Next
		}
		cur.Next = &ListNode{Val: temp % 10}
		temp /= 10
		cur = cur.Next
	}
	if temp != 0 {
		cur.Next = &ListNode{Val: temp}
	}
	return head.Next
}

链表入门题目-划分链表

        给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

        你应当 保留 两个分区中每个节点的初始相对位置。

        创建两个新链表,不断往后创建符合该链表要求的节点,创建并赋值的理由同上一题。最后让小于链表的next指向等于或大于链表头的next

func partition(head *ListNode, x int) *ListNode {
	less, more := &ListNode{}, &ListNode{}
	var lesshead, morehead  *ListNode
	lesshead = less
	morehead =more
	cur := head
	for cur != nil {
		if cur.Val < x {
			less.Next = &ListNode{Val: cur.Val}
			less = less.Next
		} else {
			more.Next = &ListNode{Val: cur.Val}
			more = more.Next
		}
        cur = cur.Next
	}
	less.Next = morehead.Next
	return lesshead.Next
}

### C语言链表入门教程 链表是一种常见的动态数据结构,用于存储和管理数据。在C语言中,链表可以分为单向链表和双向链表两种主要形式[^1]。 #### 单向链表 单向链表是最基本的链表形式,每个节点包含两部分:数据域和指针域。数据域用于存储实际数据,指针域指向下一个节点。以下是单向链表的基本定义和操作: ```c struct LNode { ElemType data; // 数据域 struct LNode* next; // 指针域 }; typedef struct LNode LNode, *LinkList; ``` ##### 基本操作 1. **创建链表** 可以通过头插法或尾插法创建链表。 2. **插入节点** 在第`i`个位置插入元素`e`的代码如下: ```c bool ListInsert(LinkList& L, int i, ElemType e) { if (i < 1) return false; LNode* p = L; // 指向当前扫描到的结点 int j = 0; // 当前指针p指向第几个结点 while (p != NULL && j < i - 1) { // 循环找到第i-1个结点 p = p->next; j++; } if (p == NULL) return false; // i值不合法 LNode* s = (LNode*)malloc(sizeof(LNode)); s->data = e; s->next = p->next; // 将结点s连接到p之后 p->next = s; // 插入成功 return true; } ``` 3. **查找节点** 查找指定数据是否存在于链表中的代码如下: ```c void search(LinkList head) { if (!head) return; int num; printf("输入要查找的数据:"); scanf("%d", &num); Node *p = head->next; while (p) { if (num == p->data) { printf("链表存在数据\n"); return; } p = p->next; } printf("不存在此数据\n"); } ``` #### 双向链表 双向链表是在单向链表的基础上增加了指向前一个节点的指针域,使得可以在两个方向上遍历链表。以下是双向链表的基本定义和操作: ```c struct DoubleLinkedListNode { ElemType data; // 数据域 struct DoubleLinkedListNode* next; // 后继指针 struct DoubleLinkedListNode* prev; // 前驱指针 }; typedef struct DoubleLinkedListNode DoubleLinkedListNode; ``` ##### 基本操作 1. **打印双向链表** 遍历并打印双向链表中的所有数据: ```c void printDoubleLinkedList(DoubleLinkedList* list) { if (list == NULL) return; if (list->head == NULL || list->head->next == NULL) return; DoubleLinkedListNode* current = list->head->next; while (current != list->tail) { printf("%d ", current->data); current = current->next; } printf("\n"); } ``` 2. **插入节点** 在双向链表中插入节点时,需要同时更新前驱和后继指针。 #### 单向链表与双向链表的区别 | 特性 | 单向链表 | 双向链表 | |-----------------|----------------------------------|----------------------------------| | 节点结构 | 包含数据域和一个后继指针 | 包含数据域、前驱指针和后继指针 | | 内存占用 | 较小 | 较大 | | 遍历方向 | 只能从头到尾 | 可以从前到后和从后到前 | | 插入/删除效率 | 相对简单 | 稍复杂,但灵活性更高 | #### 总结 链表是一种灵活的数据结构,适用于动态数据管理场景。单向链表适合简单的顺序访问需求,而双向链表则提供了更高的灵活性,但会增加内存开销和实现复杂度[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值