反转链表

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

限制:

0 <= 节点个数 <= 5000

思路一:

虽然是链表的练习,但是算法的思路非常多。依旧我之前所说会用到各种办法,不是链表部分只用链表

本题:需要返回一个链表指针,这是一个新的链表。这个新的链表本质上是从旧的链表尾一个一个拿元素。也就是对于旧的链表而言,实际上是一个先进后出的一个情形。所以我们首先想到符合这个特点的就是栈。

从头开始压栈,压完之后一个一个出栈。放入新的链表中;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return NULL;
        if(head->next == NULL) return head;
        stack<ListNode*> stack_listnode;
        while(head != NULL) {
            stack_listnode.push(head);
            head = head->next;
        }
        ListNode* q = stack_listnode.top();
        ListNode* qHead = q;
        stack_listnode.pop();
        while(stack_listnode.size() != 0) {
            q->next = stack_listnode.top();
            stack_listnode.pop();
            q = q->next;
        }
        q->next = NULL;
        return qHead;
    }
};  

思路二:

如果你是一个认认真真学习过数据结构的同学,你一定会想到的一个思路:头插法和尾插法!

说到这里想必你已经明白了。头插和尾插会在另一个md里面详细说明;

思路三:

简单的来说,通过两个指针来在给定链表上爬,在爬的过程中给第三个指针赋值,慢慢将第三个指针构成新的链表。有点像DNA的转录过程….

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *p1,*p2,*p3;
        p1 = NULL;
        p2 = head;
        while(p2!=NULL){
            p3 = p2->next;
            p2->next = p1;
            p1 = p2;
            p2 = p3;
        }
        return p1;
    }
};

删除中间结点

面试题 02.03. 删除中间节点

难度简单31

实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点),假定你只能访问该节点。

示例:

输入:单向链表a->b->c->d->e->f中的节点c
结果:不返回任何数据,但该链表变为a->b->d->e->f

思路:

本题的关键是在于理解题意。

题意分析:

​ 题目只给你某一个结点,其他什么都不给,链表你也拿不到。让你删除这个元素,个人认为这是一个很好的案例。我拿不到链表,只能拿到结点。也不知道这是第几个…

​ 但是我们可以访问这个结点之后的结点,也可以访问之后的之后的结点。所以把本结点等于next的值,然后把next删掉,偷梁换柱。

​ 怎么删除呢?其实就是把node->next = node->next->next 直接赋值就行。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        if(node == NULL) return ;
        node->val = node->next->val; 
        node->next = node->next->next;
    }
};

回文链表

234. 回文链表

难度简单533

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?


思路一:

放进数组一个一个比较

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL)    return true;
        if(head->next == NULL)      return true;
        vector<int> array;
        int count = 0;
        while(head != NULL){
            array.push_back(head->val);
            head = head->next;
        }
        for(int i = 0;i < array.size() / 2; i++){
            if(array[i] != array[array.size() - i - 1])
                return false;
        }
        return true;
        
    }
};

思路二:

使用快慢指针,找到中间,把后半部分reverse,从中间开始比较。

class Solution {
public:
    bool isPalindrome(ListNode* head) {//O(n)、O(1)
        ListNode* slow = head, *fast = head,  *prev = nullptr;
        while (fast){//find mid node
            slow = slow->next;
            fast = fast->next ? fast->next->next: fast->next;
        }
        while (slow){//reverse
            ListNode* temp = slow->next;
            slow->next = prev;
            prev = slow;
            slow = temp;
        }
        while (head && prev){//check
            if (head->val != prev->val){
                return false;
            }
            head = head->next;
            prev = prev->next;
        }
        return true;
    }
};

链表求和

面试题 02.05. 链表求和

难度中等23

给定两个用链表表示的整数,每个节点包含一个数位。

这些数位是反向存放的,也就是个位排在链表首部。

编写函数对这两个整数求和,并用链表形式返回结果。

示例:

输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

进阶:假设这些数位是正向存放的,请再做一遍。

示例:

输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912

思路:

​ 本题的思路很明确:链表的合并+进位问题。(链表的合并,会在链表专题中专门讲)

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *sum = l1;
        while(l1->next && l2->next)
        {
            l1->val += l2->val;
            l1 = l1->next;
            l2 = l2->next;
        }
        l1->val += l2->val;
        if(!l1->next)
        {
            l1->next = l2->next;
        }
        l1 = sum;
        while(l1->next)
        {
            if(l1->val >= 10)
            {
                l1->val -= 10;
                l1 = l1->next;
                l1->val++;
            }
            else
            {
                l1 = l1->next;
            }
        }
        if(l1->val >= 10)
        {
            l1->val -= 10;
            l1->next = new ListNode(1);
            return sum;  
        }
        else
        {
            return sum;
        }
    }       
};

PAT A1032(寻找第一个公共节点)

题目

1032 Sharing (25分)

To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For example, loading and being are stored as showed in Figure 1.

fig.jpg

Figure 1

You are supposed to find the starting position of the common suffix (e.g. the position of i in Figure 1).

Input Specification:

Each input file contains one test case. For each case, the first line contains two addresses of nodes and a positive N (≤105), where the two addresses are the addresses of the first nodes of the two words, and N is the total number of nodes. The address of a node is a 5-digit positive integer, and NULL is represented by −1.

Then N lines follow, each describes a node in the format:

Address Data Next

whereAddress is the position of the node, Data is the letter contained by this node which is an English letter chosen from { a-z, A-Z }, and Next is the position of the next node.

Output Specification:

For each case, simply output the 5-digit starting position of the common suffix. If the two words have no common suffix, output -1 instead.

Sample Input 1:
11111 22222 9
67890 i 00002
00010 a 12345
00003 g -1
12345 D 67890
00002 n 00003
22222 B 23456
11111 L 00001
23456 e 67890
00001 o 00010
Sample Output 1:
67890
Sample Input 2:
00001 00002 4
00001 a 10001
10001 s -1
00002 a 10002
10002 t -1
Sample Output 2:
-1

题⽬⼤意:求两个链表的⾸个共同结点的地址。如果没有,就输出-1

思路

⽤结构体数组存储,node[i]表示地址为i的结点,key表示值,next为下⼀个结点的地址,flag表

示第⼀条链表有没有该结点

遍历第⼀条链表,将访问过的结点的flag都标记为true,当遍历第⼆条结点的时候,如果遇到了true的

结点就输出并结束程序,没有遇到就输出-1

Code


#include <cstdio>
using namespace std;
const int MAX = 100010;
struct node
{
    char data;
    int next;
    bool flag;
}node[MAX];
int main()
{
    for (int i = 0; i < MAX; i++)
    {
        node[i].flag = false;
    }
    int s1, s2, n;//s1和s2分别是两条链表的首地址
    scanf("%d%d%d",&s1, &s2, &n);
    int address, next;//节点地址和后继结点地址
    char data;
    for (int i = 0; i < n; i++)
    {
        scanf("%d %c %d", &address, &data, &next);
        node[address].data = data;
        node[address].next = next;
    }
    int p;
    for (p = s1; p != -1; p = node[p].next)
    {
        node[p].flag = true;
    }
    for (p = s2; p != -1; p = node[p].next)
    {
        if (node[p].flag == true)
        {
            break;
        }
    }
    if (p != -1)
    {
        printf("%05d\n", p);
    }
    else
    {
        printf("-1\n");
    }

    return 0;
}

PAT A1097(删除绝对值相同的节点)

题目

1097 Deduplication on a Linked List (25分)

Given a singly linked list L with integer keys, you are supposed to remove the nodes with duplicated absolute values of the keys. That is, for each value K, only the first node of which the value or absolute value of its key equals K will be kept. At the mean time, all the removed nodes must be kept in a separate list. For example, given L being 21→-15→-15→-7→15, you must output 21→-15→-7, and the removed list -15→15.

Input Specification:

Each input file contains one test case. For each case, the first line contains the address of the first node, and a positive N (≤105) which is the total number of nodes. The address of a node is a 5-digit nonnegative integer, and NULL is represented by −1.

Then N lines follow, each describes a node in the format:

Address Key Next

where Address is the position of the node, Key is an integer of which absolute value is no more than 104, and Next is the position of the next node.

Output Specification:

For each case, output the resulting linked list first, then the removed list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input:
00100 5
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854
Sample Output:
00100 21 23854
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1

题⽬⼤意:给⼀个链表,去重(去掉值或者绝对值相等的),先输出删除后的链表,再输出删除了的链表。

思路

⽤结构体数组存储这个链表,⼤⼩为maxn = 100000,node[i]表示地址为i的结点。在结构体中定义⼀个num变量,将num变量先初始化为2 * maxn。通过改变num变量的值最后sort排序来改变链表的
顺序。
将没有删除的结点的num标记为cnt1,cnt1为当前没有删除的结点的个数;将需要删除的结点的num标记为maxn + cnt2,cnt2表示当前删除了的结点的个数,因为⼀开始初始化为了2 * maxn,所以我们可
以通过对num排序达到:num = 0~maxn为不删除结点,num = maxn~2maxn为删除结点,num = 2maxn为⽆效结点
这样sort后就会按照需要输出的顺序将结点排序,我们只需要输出前cnt1+cnt2个结点即可~~

code

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100000;
struct Node {
    int address, key, next, num = 2 * maxn;
}node[maxn];
bool exist[maxn];
int cmp1(Node a, Node b) {
    return a.num < b.num;
}
int main() {
    int begin, n, cnt1 = 0, cnt2 = 0, a;
    scanf("%d%d", &begin, &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a);
        scanf("%d%d", node[a].key, &node[a].next);
        node[a].address = a;
    }
    for (int i = begin; i != -1; i = node[i].next) {
        if (exist[abs(node[i].key)] == false) {
            exist[abs(node[i].key)] = true;
            node[i].num = cnt1;
            cnt1++;
        }
        else {
            node[i].num = maxn + cnt2;
            cnt2++;
        }
    }
    sort(node, node + maxn, cmp1);
    int cnt = cnt1 + cnt2;
    for (int i = 0; i < cnt; i++) {
        if (i != cnt1 - 1 && i != cnt - 1) {
            printf("%05d %d %05d\n", node[i].address, node[i].key, node[i + 1].address);
        }
        else {
            printf("%05d %d -1\n", node[i].address, node[i].key);
        }
    
    }
    return 0;
}

Horizon
3 声望1 粉丝

今日潜修默进,是为来日天下行走