数据结构|链表

节点

我们定义一个节点,这个节点包括该节点的值与下一个节点的地址,节点是分散不连续保存在内存中的,在C++中我们可以通过构造函数的方式快速新建一个节点。

struct ListNode
{
    //节点保存的值
    int val;
    //用于指向下一个节点
    ListNode *next;
    //节点构造函数
    ListNode(int x) : val(x), next(NULL){};
};

用一个类实现这个数据结构

成员变量

我们需要一个头指针用来标记这个链表在内存中第一个节点的位置,并利用第一个节点保存下一个节点地址的特性,就可以以此类推,遍历整个链表。同时,我们完全可以用一个变量来同步保存整个链表中节点的数量,这样,就可以更快的判断越界访问或者其他的很多行为。

private:
    // 保存第一个节点的地址
    ListNode *head = NULL;
    // 记录链表长度
    int size = 0;

增删改查操作方法的实现

插入一个元素到链表末尾

实现思路:

  • 如果size等于零,那么直接创建一个节点,并让头指针保存这个节点的地址即可。
  • 如果size不为零,则通过迭代的方式找到最后一个节点,并创建新的节点,在最后这个节点中保存新节点的地址。
void InsertEnd(int val)
    {
        if (size == 0)
        {
            // 第一个节点不存在时直接构造节点
            head = new ListNode(val);
            size++;
            return;
        }
        // 临时变量保存第一个节点地址
        ListNode *current = head;
        while (current->next != NULL)
        {
            current = current->next;
        }

        current->next = new ListNode(val);
        size++;
    }

插入一个元素到头部

void InsertHead(int val)
    {
        size++;
        ListNode *inser = new ListNode(val);
        inser->next = head;
        head = inser;
    }

插入一个元素到指定位置

实现思路:

  • 如果插入的位置大于链表节点数,报错退出
  • 如果插入的位置小于等于零,则直接调用插入到头部的方法
void InsertToAny(int val, int loca)
    {
        if (loca > size)
        {
            cout << "Error loca to big" << endl;
            return;
        }
        if (loca <= 0)
        {
            // 如果位置小于等于0直接插入到头部
            this->InsertHead(val);
            size++;
            return;
        }

        ListNode *inser = new ListNode(val);
        ListNode *current = head;
        while (loca > 1)
        {
            current = current->next;
            loca--;
        }
        // ListNode *next = current->next;
        // current->next = inser;
        // inser->next = next;
        //这里的current为待插入位置的上一个节点
        inser->next = current->next;
        current->next = inser;


        size++;
    }

删除指定位置的节点

实现思路:

  • 如果删除的位置大于链表节点数或者小于零,报错退出
  • 同样遍历到要删除的位置的节点的上一个节点,让它直接指向要删除的节点,并释放需要删除的节点。
void DelAny(int loca)
    {
        if (loca > size)
        {
            cout << "Error loca to big" << endl;
            return;
        }
        if (loca < 0)
        {
            cout << "Error loca not < 0" << endl;
            return;
        }

        ListNode *current = head;

        if (loca == 0)
        {
            head = head->next;
            delete current;
            size--;
            return;
        }

        while (loca > 1)
        {
            current = current->next;
            loca--;
        }

        ListNode *next = current->next->next;
        delete current->next;
        current->next = next;

        size--;
    }

获取任意位置的节点值

int GetAny(int loca)
    {
        if (loca >= size)
        {
            cout << "Error loca to big" << endl;
            return 0;
        }
        if (loca < 0)
        {
            cout << "Error loca not < 0" << endl;
            return 0;
        }

        ListNode *current = head;

        if (loca == 0)
        {
            return head->val;
        }

        while (loca > 0)
        {
            current = current->next;
            loca--;
        }

        return current->val;
    }

获取节点数

    int Size()
    {
        return size;
    }

将链表转为String

string ToString()
    {
        string s;
        ListNode *current = head;
        while (current != NULL)
        {
            s += to_string(current->val) + " ";
            current = current->next;
        }

        return s;
    }

排序这个链表

使用冒泡排序算法实现

void Sort()
    {
        // 使用冒泡排序 只做值排序
        ListNode *current = head;
        for (int i = size; i > 0; i--)
        {
            for (int j = 0; j < i - 2; j++)
            {
                if (current->next->val < current->val)
                {
                    int temp = current->val;
                    current->val = current->next->val;
                    current->next->val = temp;
                }
                current = current->next;
            }
            // 重新将地址恢复到第一个节点
            current = head;
        }
    }

测试

int main()
{
    List list;

    list.InsertEnd(7);
    list.InsertEnd(34);
    list.InsertEnd(1);
    list.InsertEnd(13);
    list.InsertEnd(5);
    list.InsertEnd(9);
    list.InsertEnd(4);

    cout << "size=" << list.Size() << endl;
    list.InsertToAny(100, 0);
    cout << "size=" << list.Size() << " " << list.ToString() << endl;

    list.DelAny(1);
    cout << "size=" << list.Size() << endl;
    list.Sort();
    cout << list.ToString() << endl;
    cout << list.GetAny(5);
}

输出

PS D:\dataCode\code> g++ .\NewList.cpp   
PS D:\dataCode\code> ./a
size=7
size=9 100 7 34 1 13 5 9 4 
size=8
1 4 5 9 13 34 100 
34
PS D:\dataCode\code> 

练习

leetcode P206 反转链表

思路是在迭代过程中先暂存好下一个节点的地址,再重置当前节点的下一个节点为上个节点,将上一个节点重置为当前节点。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
    var prev *ListNode = nil
    for head!=nil {
        curr := head.Next
        head.Next = prev
        prev = head
        head = curr
    }

    return prev
}

leetcode P141 环形链表

可以使用哈希表保存遍历过的所有节点,如果发现有相同的节点地址出现,则为循环链表。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func hasCycle(head *ListNode) bool {
    hash := make(map[*ListNode]any)
    for head!=nil {
        _,ok := hash[head.Next]
        if ok {
            return true
        } else {
            hash[head.Next] = struct{}{}
        }
        head = head.Next
    }

    return false
}

leetcode P202 快乐数

这道题很有意思,从题面中可以看到两个条件

  • 每次迭代的数由上一个数唯一确定,且不与上一个数相等.
  • 要么无限循环,要么为1.

如果无限循环加上条件一,也就意味着,这个数与前面迭代过的数重复了 ,造成了无限循环,如果不重复,那么一定会迭代到1位置,这样其实思路与环形链表差不多。

func isHappy(n int) bool {
    hash := make(map[int]any)
    for {
        if n==1 {
            return true
        }
        var y int
        for n!=0 {
            d := n%10
            y += d*d
            n /= 10
        }
        _,ok := hash[y]
        if ok {
            return false
        } else {
            hash[y] = struct{}{}
        }
        n = y
    }
}

leetcode P61 旋转链表

移动N个位置,可以理解为把链表首位相连,从指定位置断开的操作,并且移动N个位置和移动N%sz(链表长度)的效果是一样的。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func rotateRight(head *ListNode, k int) *ListNode {
    if head==nil {
        return head
    }
    currhead := head
    var size int
    for head!=nil {
        if head.Next == nil {
            head.Next = currhead
            size++
            break
        }
        head = head.Next
        size++
    }
    
    nextnum := k%size
    var reshead *ListNode
    for i:=0;i<size-nextnum-1;i++ {
        currhead = currhead.Next
    }
    reshead = currhead.Next
    currhead.Next = nil

    return reshead
}

leetcode P19 删除链表的倒数第 N 个结点

这题可以使用快慢指针,两个间隔n距离的节点,快节点走到nil的时候直接删除中间的节点。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    if head.Next == nil {
        return nil
    }

    currhead := head
    walk1 := head
    walk2 := head
    n-=1
    for n!=0 {
        walk2 = walk2.Next
        n-=1
    }
    //考虑删除的是第一个节点的情况
    if walk2.Next == nil {
        return walk1.Next
    }

    walk2 = walk2.Next

    for {
        if walk2.Next == nil {
            walk1.Next =  walk1.Next.Next
            return currhead
        }
        walk1 = walk1.Next
        walk2 = walk2.Next
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇