1.[21]合并两个有序链表
解题记录:
1.因为两个链表均有序排列,故设置头结点及其指针,两个链表依次比较,较小结点尾插新链表。
2.新建指针时无需分配空间,新建结点时需要malloc分配空间。
3.其中一个链表为空时,可以直接输出另一个链表,此为特例情况,也可以不增设,因为在while循环后加上了if判断,使得输出结果一致。
4.因为设置的新链表有头结点,输出应为next结点,也就是第一个结点。
C语言代码:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
struct ListNode *h = (struct ListNode*) malloc (sizeof(struct ListNode)); //新链表头结点
struct ListNode *t = h, *p = l1, *q = l2;
if (!p) return q; //特例情况,可有可无
if (!q) return p;
while (p && q) //p和q都不为空时向后遍历
{
if (p->val <= q->val) //选出较小值插入h
{
t->next = p;
p = p->next;
}
else
{
t->next = q;
q = q->next;
}
t = t->next; //指针后移
}
if (p) t->next = p; //若没遍历完,则全部插入
else t->next = q;
return h->next; //h是头结点,第一个结点是h->next
}
2.[83]删除排序链表中的重复元素
解题记录:
1.因为链表有序排列,故设置指针指向第一个结点,与next结点依次比较,若重复则将next指针指向下一个。
2.因为默认有next指针,故要另外分析空链表的情况,直接返回链表。
3.在while循环中需先判断next结点不为空,否则无数据比较。
4.遍历过程中改变next指针的指向,最后直接输出修改后的链表即可。
C语言代码:
struct ListNode* deleteDuplicates(struct ListNode* head){
struct ListNode *p = head;
if (!p) return head; //若为空节点,直接返回
while (p->next) //比较的前提是p->next不为空
{
if (p->next->val == p->val) //若元素重复,则跳过该元素
p->next = p->next->next;
else //若元素不重复,则p向后移动
p = p->next;
}
return head;
}
3.[160]相交链表
解题记录:
1.指针p遍历完A后遍历B,指针q遍历完B后遍历A,最后遍历的总长是相等的,都是lenA+lenB,因此在p和q都进入另一个链表遍历时,它们到遍历结束的距离是相等的,所以两个链表相交时,一定能在遍历完之前满足p == q的条件。
2.若两个链表没有交点,那么退出时p和q相等,均为NULL,返回p也就是返回NULL,满足题目要求。
3.每个指针最多需要遍历lenA+lenB个结点,故时间复杂度为O(n)。
C语言代码:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *p = headA;
struct ListNode *q = headB;
while (p != q)
{
p = ((!p) ? headB : p->next);//遍历完A遍历B
q = ((!q) ? headA : q->next);//遍历完B遍历A
}//由于总长lenA+lenB,当p和q都进入第二次遍历时,它们到遍历结束的距离相同
return p;//当p == q时,找到相交结点,若是p == q == null时退出,范围值为null
}
4.[206]反转链表
解题记录:
1.设置前一节点pre、当前结点p和下一节点next,当当前结点不为空时,将原next指针指向pre,实现反转,然后pre和p依次后移,遍历并反转。
2.设置新的next指针是为了防止断链。
3.结束遍历时输出pre指针,是新链表的第一个结点,也是原链表的最后一个结点,而此时p结点指向NULL。
C语言代码:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode *pre = 0, *p = head; //pre指向当前结点的前一个结点,初始为空,p指向当前结点,初始为头结点
while (p) //p不为空时进行反转操作
{
struct ListNode *next = p->next; //设置next指针防止断链
p->next = pre; //反转操作
pre = p; //后移操作
p = next;
}
return pre;
}
5.[234]回文链表
解题记录:
1.采用快慢指针遍历的方法,找到中间结点,反转后半部分结点并与原链表逐一比较。
2.考虑到奇偶个数结点的不同情况,需要判断fast->next和fast->next->next均不为空,因此空链表和一/二个数结点的链表需要单独考虑。
3.反转链表操作见206题。
4.“&&”与操作的判断是先判断左边条件,因此fast->next->next判断不能放在前面,会导致空指针。
C语言代码:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode *pre = NULL, *p = head; //pre指向当前结点的前一个结点,初始为空,p指向当前结点,初始为头结点
while (p) //p不为空时进行反转操作
{
struct ListNode *next = p->next; //设置next指针防止断链
p->next = pre; //反转操作
pre = p; //后移操作
p = next;
}
return pre;
}
bool isPalindrome(struct ListNode* head){
struct ListNode *fast = head, *slow = head; //定义快慢指针
if (!head || !head->next) return true; //空链表或仅有一结点的链表,无须比较
if (!head->next->next) //当只有两个结点时,另外讨论
{
if (head->val == head->next->val) return true;
else return false;
}
while (fast->next && fast->next->next) //判断条件分别对应奇数个数结点和偶数个数结点
{
slow = slow->next; //慢指针一次走一格,快指针一次走两格,结束循环时慢指针指向中间
fast = fast->next->next;
}
slow = reverseList(slow->next); //反转后半部分链表
while (slow) //将反转后的后半部分链表与head逐一比较
{
if (slow->val != head->val)
return false;
slow = slow->next;
head = head->next;
}
return true; //每个结点都相等返回true
}