算法1:反转链表
给定单链表的头结点head,请你反转链表,并返回反转后的链表
思路1:翻指针方向
思路2:头插法
思路1:翻指针方向
struct ListNode* reverseList(struct ListNode* head) {
if (head == NULL) {
return NULL;
}
//初始条件
struct ListNode* n1 = NULL, * n2 = head, * n3 = n2->next;
//结束条件
while (n2) {
//迭代过程
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
思路2:头插法
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* cur = head;
struct ListNode* newHead = NULL;
while (cur) {
struct ListNode* next = cur->next;
//头插
cur->next = newHead;
newHead = cur;
cur = next;
}
return newHead;
}
算法2:链表的中间结点
给定一个头结点为head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
示例:
输入:[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, * fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
算法3:合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。
思路:从头开始,去两个链表中小的那个尾插到新链表
示例:
方法一:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
while (l1 != NULL&&l2 != NULL) {
if (l1->val < l2->val) {
if (tail = NULL) {
head = tail = l1;
}
else {
tail->next = l1;
tail = tail->next;
}
l1 = l1->next;
}
else {
if (tail = NULL) {
head = tail = l2;
}
else {
tail->next = l2;
tail = tail->next;
}
l2 = l2->next;
}
}
if (l1) {
tail->next = l1;
}
if (l2) {
tail->next = l2;
}
return head;
}
方法二:将第一个结点提出来写
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
if (l1->val < l2->val) {
head = tail = l1;
l1 = l1->next;
}
else {
head = tail = l2;
l2 = l2->next;
}
while (l1 != NULL && l2 != NULL) {
if (l1->val < l2->val) {
tail->next = l1;
l1 = l1->next;
}
else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
if (l1) {
tail->next = l1;
}
if (l2) {
tail->next = l2;
}
return head;
}
方法三:定义哨兵结点
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
//哨兵位
head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
while ( l1 != NULL && l2 != NULL) {
if (l1->val < l2->val) {
tail->next = l1;
l1 = l1->next;
}
else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
if (l1) {
tail->next = l1;
}
if (l2) {
tail->next = l2;
}
struct ListNode* first = head->next;
free(head);
return first;
}
算法4:环形链表
给定一个链表,判断链表中是否有环,如果链表中有某个结点,可以通过连续跟踪next指针再次到达,则链表中存在环。为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。如果pos是-1,则在该链表中没有环。注意:pos不作为参数进行传递,仅仅是为了标识链表的实际情况。如果链表中存在环,则返回True。否则,返回False。
思路分析:定义快慢指针(slow和fast),slow每次走一步,fast每次走两步。
问题1:slow每次走一步,fast每次走两步一定会在环中相遇吗?为什么?
问题2:slow每次走一步,fast每次走三步一定会在环中相遇吗?为什么?
问题2:slow每次走一步,fast每次走四步一定会在环中相遇吗?为什么?结论:slow每次走一步,fast每次走两步一定会在环中相遇,slow进环之后,fast开始追,假设fast和slow之间的距离是N,在追的过程中,他们的距离每次会缩短1,因此最终会减少为0,距离为0的时候相遇。如果slow每次走一步,fast每次走三步不一定会在环中相遇,slow进入环后,fast开始追slow,假设他们之间的距离是N,那么fast和slow都各走一步,两者的距离缩短2步,如果N为偶数是可以追到,如果N为奇数时直接从距离为1到-1,代表fast反超了slow而并没有相遇,就进入了新的追逐过程,他们之间的距离是C-1(假设C是环的长度),如果C-1是偶数,就能追上,如果C-1是奇数,那就永远追不上。因为距离是奇数就意味着快追上时,又会距离为-1,然后fast翻炒slow,这里的距离又是C-1,那么就死循环了,永远追不上了。同理slow每次走一步,fast每次走四步不一定会在环中相遇。
bool hasCycle(struct ListNode* head) {
struct ListNode* slow = head, * fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
return true;//相遇
}
return false;
}