数组 + 矩阵 + 串是常见的数据结构,需要深入掌握与学习。
对于数组:根据指定下标定位相关元素的地址是最为关键的操作,玩数组就是玩下标。
矩阵:M * N 的二维数组,是逻辑线性结构。
模型如下:
稀疏矩阵:矩阵中绝大多数元素为零,只有少量非零元素;稀疏因子 = 非零元素数量/矩阵总元素数量。
存储空间利用率:有效元素所申请空间/该矩阵所申请的空间。
定位:当给出下标i、j,且i、j在有效下标取值范围内,需要定位其所对应的元素在压缩存储方式中的位置。
为提升空间利用率(只存储有效元素,避免空间过度浪费),矩阵的存储一般采用三元组法/十字交叉链。
typedef struct TRIAD{
int value;
int row;
int col;
}TRIAD;
typedef struct TRIPLE{
TRIAD *element;
int rowCount;
int colCount;
int elementCount;
}TRIPLE;
#include <stdio.h>
#include <malloc.h>
typedef struct TRIAD{
int value;
int row;
int col;
}TRIAD;
typedef struct TRIPLE{
TRIAD *element;
int rowCount;
int colCount;
int elementCount;
}TRIPLE;
TRIPLE *inputTriple(void);
void destoryTriple(TRIPLE *);
void showMatrix(TRIPLE triple);
TRIPLE *revangeMatrix(TRIPLE triple);
TRIPLE *initTriple(int rowCount, int colCount, int elementCount);
TRIPLE *initTriple(int rowCount, int colCount, int elementCount){
TRIPLE *triple;
triple = (TRIPLE *)malloc(sizeof(TRIPLE));
triple->rowCount = rowCount;
triple->colCount = colCount;
triple->elementCount = elementCount;
triple->element = (TRIAD *)malloc(sizeof(TRIAD) * elementCount);
return triple;
}
TRIPLE *revangeMatrix(TRIPLE triple){
TRIPLE *revTriple;
int *ec;
int i;
int index;
revTriple = initTriple(triple.rowCount, triple.colCount, triple.elementCount);
revTriple->rowCount = triple.colCount;
revTriple->colCount = triple.rowCount;
revTriple->elementCount = triple.elementCount;
ec = (int *)calloc(triple.colCount+1, sizeof(int));
for(i = 0; i < triple.elementCount; i++){
ec[triple.element[i].col + 1]++;
}
for(i = 1; i < triple.colCount+1; i++){
ec[i] += ec[i-1];
}
for(i = 0; i < triple.elementCount; i++){
index = ec[triple.element[i].col];
revTriple->element[index].col = triple.element[i].row;
revTriple->element[index].row = triple.element[i].col;
revTriple->element[index].value = triple.element[i].value;
ec[index+1]++;
}
free(ec);
return revTriple;
}
void showMatrix(TRIPLE triple){
int i;
int j;
int t = 0;
for(i = 0; i < triple.rowCount; i++){
for(j = 0; j < triple.colCount; j++){
if(i == triple.element[t].row && j == triple.element[t].col)
printf("%d\t", triple.element[t++].value);
else
printf("0\t");
}
printf("\n");
}
}
void destoryTriple(TRIPLE *triple){
free(triple->element);
free(triple);
}
TRIPLE *inputTriple(void){
TRIPLE *triple;
int rowCount;
int colCount;
int elementCount;
int i;
scanf("%d%d%d", &rowCount, &colCount, &elementCount);
triple = initTriple(rowCount, colCount, elementCount);
for(i = 0; i < elementCount; i++){
int value;
int row;
int col;
scanf("%d%d%d", &row, &col, &value);
triple->element[i].row = row;
triple->element[i].col = col;
triple->element[i].value = value;
}
return triple;
}
void main(void){
TRIPLE *tr;
TRIPLE *revTr;
tr = inputTriple();
revTr = revangeMatrix(*tr);
showMatrix(*tr);
showMatrix(*revTr);
destoryTriple(tr);
destoryTriple(revTr);
}
// 1、稀疏矩阵中的每一个元素的数据类型是 USER_TYPE,用户自定义数据类型
typedef int USER_TYPE;
// 2、十字交叉链中每一个节点的数据类型
typedef struct NODE{
USER_TYPE data;
struct NODE *nextCol;
struct NODE *nextRow;
}NODE;
// 3、无论是行链表、列链表,都应该是多个链表,且用头指针作为起点
typedef struct CROSS_LINK{
int rowCount;
int colCount;
NODE **rowHead;
NODE **colHead;
}CROSS_LINK;
CROSS_LINK *crossHead = NULL; //通过头结点指针实现逻辑
具体代码实现较为复杂,暂时还没时间去实现这块具体的逻辑,有兴趣的读者,可以自行实现代码细节。
串:数组 + 链表的结合是其基本组成结构。
涉及到经典算法:串匹配基础算法 --> KMP 串匹配算法(后续会写这个算法)
使用数组表达串的缺陷:
- 长度不能够灵活变化;
- 对于串中字符的插入、删除操作存在着大量的移动操作。
随机访问与顺序访问:
- 随机访问是无需遍历直接定位的访问方式,通常指下标访问;
- 顺序访问是必须重头开始逐一遍历的访问方式,通常指链表节点访问。
串的常见操作有:初始化串、销毁串、指定下标插入、删除、查找、替换、复制、连接、分割等等操作。
#define UNIT_LEN 16
// 1、每一个节点的数据类型
typedef struct STRING_UNIT{
char string[UNIT_LEN];
struct STRING_UNIT *next;
char len;
}STRING_UNIT;
// 2、头指针进行控制
typedef struct STRING{
STRING_UNIT *stringHead;
int length;
}STRING;
#ifndef _MEC_STRING_H_
#define _MEC_STRING_H_
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define UNIT_LEN 16
typedef struct STRING_UNIT{
char string[UNIT_LEN];
struct STRING_UNIT *next;
char len;
}STRING_UNIT;
typedef struct STRING{
STRING_UNIT *stringHead;
int length;
}STRING;
boolean initString(STRING **string);
STRING_UNIT *creatStringUnit();
void destoryString(STRING **string);
void removeStringUintAt(STRING_UNIT *curUnit);
void setStringUnitContent(STRING_UNIT *target, const char *source, int len);
void stringCopy(STRING *string, const char *source);
void showString(STRING string);
void showString(STRING string){
int count = 0;
int index = 0;
STRING_UNIT *cur;
register char ch;
cur = string.stringHead;
while(cur && count < string.length){
ch = cur->string[index];
if(ch){
putchar(ch);
count++;
if(++index >= UNIT_LEN){
index = 0;
cur = cur->next;
}
}
}
}
void stringCopy(STRING *string, const char *source){
STRING_UNIT *cur;
int sourceLen;
int dealedCount = 0;
sourceLen = strlen(source);
string->length = sourceLen;
cur = string->stringHead;
while(sourceLen > 0){
setStringUnitContent(cur, source + dealedCount++ * UNIT_LEN,
sourceLen > UNIT_LEN ? UNIT_LEN : sourceLen);
sourceLen -= UNIT_LEN;
if(sourceLen > 0){
if(cur->next == NULL){
cur->next = creatStringUnit();
}
cur = cur->next;
}
}
while(cur->next){
removeStringUintAt(cur);
}
}
void setStringUnitContent(STRING_UNIT *target, const char *source, int len){
int index;
for(index = 0; index < len; index++){
target->string[index] = source[index];
}
while(index < UNIT_LEN){
target->string[index++] = 0;
}
target->len = len;
}
void removeStringUintAt(STRING_UNIT *curUnit){
STRING_UNIT *p;
if(curUnit->next){
p = curUnit->next;
curUnit->next = p->next;
free(p);
}
}
void destoryString(STRING **string){
STRING_UNIT *head;
if(!*string){
return;
}
head = (*string)->stringHead;
while(head){
STRING_UNIT *p;
p = head;
head = head->next;
free(p);
}
free(*string);
*string = NULL;
}
STRING_UNIT *creatStringUnit(){
STRING_UNIT *head;
head = (STRING_UNIT *)calloc(sizeof(STRING_UNIT), 1);
head->len = 0;
head->next = NULL;
return head;
}
boolean initString(STRING **string){
if(*string){
return FALSE;
}
*string = (STRING *)malloc(sizeof(STRING));
(*string)->stringHead = creatStringUnit();
(*string)->length = 0;
return TRUE;
}
#endif
三元组、十字交叉链、KMP 算法、串(数组+链表)实现方式是数据结构中最基础部分,这部分需要消化吃透。
原创文章链接:从零开始学习数据结构-->矩阵+串