【数据结构学习记录10】——串

一.简述

1.前言

书上说的:“在处理字符串数据时,会比处理整数或浮点数要复杂的多,且在不同类型的应用中,所用的字符串有不同的特点。所以要想有效的实现字符串处理,就必须根据具体的情况使用合适的储存结构。”我想,这因该是这一章存在的意义吧。

2.性质定义

是由零个活多个字符组成的有限序列,一般记为:
s = a0a1a2···an (n>=0)
s是串的名;用单引号括起来的字符序列是串的值;a可以是字母,也可以是其他字符,串中字符数目n称为串的长度。零个字符的串称为空串
串中,任意个连续的字符组成的字序列称为该串的子串;包含子串的串相应地被称为主串;通常称字符在序列中的序号为该字符串在串中的位置.
举例:
s = ‘kanna’
b = ‘anna’
c = ‘kan’
那么字符串b,c是a的子串,且s,b,c的长度分别为:5,4,3。b在s里的位置是2,c在s里的位置是1。

二.实现

1.简述串

除了我们基本的增删改查这几个函数,我们还得构思几个函数:串的长度;返回指定位置、指定长度的字串;字串的查找……

2.串的结构

本来书上还有一个采用数组来表示串的结构,但和我们这个堆分配(malloc)大同小异,所以我们着重研究后者。
对于串的结构,我们还是使用两个部分:用于储存文本的基址区 长度
在这里插入图片描述

3.串的初始化

我们可以用C语言风格的字符串数组,或者字符串字面值来初始化一个串,C语言风格的字符串有个显著的特点,那就是串的末尾会有一个\0来标记串的结束。那么通过便利来获取字串的长度,然后再一个一个赋值初始化着恶搞串。

4.串的"加法"

当然,串的加法就相当于把字串1不变,把字串2的内容拼接到字串1后面,并将字串1的长度加上字串2的长度。
在这里插入图片描述

5.串的比较

我们先比较两个串的长度,如果长度都不一样,那么两个串肯定不一样。如果两个串长度一样,那么开始比较两个串的第一个字符,第二个字符……,直到第一个不相同的字符,那就说明不相等,如果到了末尾都还没有不同的字符,那就说明两个串是一样的。

6.串的销毁

因为我们的串是动态生成的,所以我们直接free掉就行惹。

7.返回字串

我们还是通过移动我们的指针,然后返回一个依次赋值的字串。

8.查找字串

查找字串有很多种方法,这里有一个时间复杂度为n*m级别的查找算法,至于优化,是后一节的内容。我们这里的基础算法如下:
有两个指针,选择指针a和匹配指针b。
我们选择指针先移动一个位置,然后匹配指针开始移动,如果匹配指针移动了字串的长度,都还一样的话,那么说明选择指针a的位置,就是字串开始的位置。如果在匹配指针移动字串长度个之前的位置,那么就说明匹配失败,选择指针a++,且b = a,并重新移动b指针。
在这里插入图片描述

三.代码

#include <stdio.h>
#include <stdlib.h>

#define     OK          1
#define     ERROR       0

typedef struct sstring{
    
    
    char *ch;
    int len;
}sstring;

sstring* StringInit(char* str);
int StringShow(sstring *str);
int StringConcat(sstring* str1, sstring* str2);
int StringCompare(sstring *str1, sstring *str2);
sstring *StringGet(sstring *str, int index, int len);
int StringFind(sstring *bstr, sstring *mstr);

int main()
{
    
    
    // 主函数随便改
    sstring *test1 = StringInit("bbcd");
    sstring *test2 = StringInit("bcd");
    //StringConcat(test1, test2);
    //printf("%d", StringCompare(test1, test2));
    //StringShow(StringGet(test2,0,3));
    printf("%d", StringFind(test1,test2));
    return 0;
}

sstring* StringInit(char* ss)
{
    
    
    int lenth = 0;
    //首先创建要返回的
    sstring *str = (sstring*)malloc(sizeof(sstring));
    
    //动态生成失败,直接退出
    if (str == NULL) exit(1);
    //如果传入的是空字符串,我们就返回一个空的字符串
    if (ss == NULL)
    {
    
    
        str->ch = NULL;
        str->len = 0;
        return str;
    }
    // 通过依次遍历,获得传入字符串中,非/0部分长度。
    while(*(ss + lenth) != '\0')
    {
    
    
        ++lenth;
    }
    // 修改我们字符串的长度和动态分配它的储存空间
    str->len = lenth;
    str->ch = (char*)malloc(sizeof(char)*lenth);
    --lenth;
    // 通过遍历,将C语言字符串的内容,复制到我们的新字符串中
    while(lenth >= 0)
    {
    
    
        *(str->ch+lenth) = *(ss+lenth);
        --lenth;
    }

    return str;
}

int StringShow(sstring *str)
{
    
    
    int ptr = 0;
    printf("the string len is %d context is: ", str->len);
    while(ptr < str->len)
    {
    
    
        printf("%c", *(str->ch + ptr));
        ++ptr;
    }
    printf("\n");
    return OK;
}

int StringConcat(sstring* str1, sstring* str2)
{
    
    
    sstring* stringNew = NULL;
    int ptr = 0;
    // 如果两个串的长度都是0,那就直接返回即可
    if (str1->len + str2->len == 0) 
    {
    
    
        return OK;
    }
    // 否则就先生成我们的新串,修改长度与内容
    stringNew = (sstring*)malloc(sizeof(sstring));
    stringNew->ch = (char*)malloc(sizeof(char)*(str1->len+str2->len));
    stringNew->len = str1->len+str2->len;
    // 通过循环,将str1的值写入新串
    for(;ptr < str1->len; ++ptr)
    {
    
    
        *(stringNew->ch+ptr) = *(str1->ch+ptr);
    
    }
    // 在str1写入新串的基础上,向新串写入str2
    for(ptr = 0;ptr < str2->len; ++ptr)
    {
    
    
        *(stringNew->ch+ptr+str1->len) = *(str2->ch+ptr);
    }

    // 然后这里优点坑,因为传递过来的指针是形参,并不是引用
    // 所以 我们只能把新串的值赋值给原来的串
    // 此时,传入函数字符串的地址没变,但是len变了, ch的地址变了
    *str1 = *stringNew;
    return OK;
}

int StringCompare(sstring *str1, sstring *str2)
{
    
    
    int i = 0;

    // 长度都不一样,所以通过长度,反应关系
    if (str1->len > str2->len)
    {
    
    
        return 1;
    }
    else if (str1->len < str2->len)
    {
    
    
        return -1;
    }
    else
    {
    
    
        // 长度一样了,只有依次对比了
        for (; i < str1->len; ++i)
        {
    
    
            // 只要有一个字符不一样,那就根据ascii的关系去返回大小关系
            if (*(str1->ch+i) < *(str2->ch+i))
            {
    
    
                return -1;
            }
            else if (*(str1->ch+i) > *(str2->ch+i))
            {
    
    
                return 1;
            }
        }
        // 循环完了也没有找到不同,所以它俩是一样的
        return 0;
    }
}

sstring *StringGet(sstring *str, int index, int len)
{
    
    
    sstring *rstr = NULL;
    int i = 0;

    // 如果目标串的长度小于我们要求的长度,所以直接返回空的
    if (str->len < index+len)
    {
    
    
        return NULL;
    }
    else
    {
    
    
        // 动态生成我们的返回串
        rstr = (sstring *)malloc(sizeof(sstring));
        rstr->ch = (char *)malloc(sizeof(char)*str->len);
        rstr->len = len;
        // 然后把目标串里的值复制到我们的返回串里
        for (i = 0; i < len; ++i)
        {
    
    
            *(rstr->ch+i) = *(str->ch+index+i);
        }
        return rstr;
    }
    
}

int StringFind(sstring *bstr, sstring *mstr)
{
    
    
    int fptr = 0, lptr = 0;
    int mark = 0;

    // 如果我们要查找的串的长度大于了目标串,那肯定找不到的,直接返回-1
    if (bstr->len < mstr->len)
    {
    
    
        return -1;
    }
    // lptr是指向 我们目标串的开始指针
    // 它只需要从0遍历到(目标串长度-要查找的串的长度)就行了
    for (;lptr <= (bstr->len-mstr->len); ++lptr)
    {
    
    
        // mark是标记位,如果有不同,那就是1 没有不同就还是0
        mark = 0;
        // 这个是查找指针,我们要对比的内容因该是lptr+fptr
        // 它的范围是 0到查找串的长度-1
        for (fptr = 0; fptr < mstr->len; ++fptr)
        {
    
    
            // 对比的内容是 lptr+fptr
            if (*(bstr->ch+lptr+fptr) != *(mstr->ch+fptr))
            {
    
    
                // 有不同,更新标识,并跳出这一轮 fptr的遍历
                mark = 1;
                break;
            } 
        }
        // fptr遍历完了,都还没有不同的,说明找到了
        if (mark == 0)
        {
    
    
            // 那么就因该返回我们lptr的起始位置
            return lptr;
        }
    }
    // 查遍了整个串都没找到,那就只能返回 -1了
    return -1;
}

猜你喜欢

转载自blog.csdn.net/u011017694/article/details/109531835
今日推荐